Remix shared layouts, a first look

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
Search for a command to run...

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
No comments yet. Be the first to comment.
Most of you know me for my consistency, a golden arrow in my blog series. I've written 1000 articles in 1008 days! Almost an article a day, and my honeymoon was the only holiday I ever took. I'm super proud of this achievement; it has been a fantasti...

It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool. This shows in my previous explorations of Astro, Medusa, and now Vendure as well. All these products thrive in a super open, welcoming, ...

The cool part about Vendure is how easy it is to set up and how abstract each layer is. Basically, we get the following elements: External database Server Worker Admin UI Frontend While this is amazing, it also brings a bit of complexity when it co...

The previous article looked at customizing Vendure on a data and process level. In this article, we'll look at customizing emails, as they are often a big part of a webshop system. We'll be looking at two different layers of customization for customi...

Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case. In this article, I'll take a high-level look at some elements we can customize within...

One super cool aspect of Remix is that it can support multiple layouts and even sub layouts.
Let's take a look at what that means. The basic essence is that you can support certain outlets (almost like React children), which can be the wrappers of your app.
Roughly:
And these will all work top-level-down, so anything you design and add to your app will be available in admin. Anything added in admin will be available in posts and so on.
This concept is not new but super strong in making reusable elements.
Let us change what we created yesterday to reflect this. We can change the admin post section to work via a shared template.
We want to design something like this:
[sidebar][outlet]
Where the sidebar is our post admin part, and the outlet is the detailed page (the new post form).
The first thing we want to add is a file called admin.tsx in our posts folder.
This file will act as the main wrapping template for our admin post-panel.
Note: There are multiple design solutions here. This is one of them (You could also create a global admin folder, which might be better for your app)
In this admin file we want to return a layout as we described above:
export default function PostAdmin() {
return (
<div className='mx-auto max-w-4xl'>
<h1 className='my-6 mb-2 border-b-2 text-center text-3xl'>
My super cool admin
</h1>
<div className='grid grid-cols-4 gap-6'>
<nav className='col-span-4 md:col-span-1'>Sidebar</nav>
<main className='col-span-4 md:col-span-3'>Main section</main>
</div>
</div>
);
}
However, this still doesn't do much. Let's always add our posts in the sidebar to have a quick navigation method.
We can use the same loader we previously created.
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import { getPosts } from "~/models/post.server";
import { LoaderFunction } from "@remix-run/node";
type LoaderData = {
posts: Awaited<ReturnType<typeof getPosts>>;
};
export const loader: LoaderFunction = async () => {
return json({ posts: await getPosts() });
};
export default function PostAdmin() {
const { posts } = useLoaderData() as LoaderData;
return (
<div className="mx-auto max-w-4xl">
<h1 className="my-6 mb-2 border-b-2 text-center text-3xl">
My super cool admin
</h1>
<div className="grid grid-cols-4 gap-6">
<nav className="col-span-4 md:col-span-1">
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link
to={`/posts/${post.slug}`}
className="text-blue-600 underline"
>
{post.title}
</Link>
</li>
))}
</ul>
</nav>
<!-- Other code -->
</div>
</div>
);
}
We can spool up our app and visit the /posts/admin link.
Already pretty cool.
Now let's add the router outlet so we can provide the detailed screens on top of this layout.
import {Outlet, [...others] } from "@remix-run/react";
export default function PostAdmin() {
return (
<div className="mx-auto max-w-4xl">
<div className="grid grid-cols-4 gap-6">
<!-- other stuff -->
<main className="col-span-4 md:col-span-3">
<Outlet />
</main>
</div>
</div>
);
}
Anything we define after this route will be in the <Outlet> part.
For instance, let's create a new folder called admin inside our posts folder.
In there, create an index.tsx file, for which we'll render the basic starting page.
import { Link } from '@remix-run/react';
export default function AdminIndex() {
return (
<p>
<Link to='new' className='text-blue-600 underline'>
Create a New Post
</Link>
</p>
);
}
The index is pretty simple as we only show a link to the new form.
This will render the following output.

Pretty cool how this outlet can overwrite even the default page.
Now let's move our new.tsx file into this admin folder and see what changes.

And even better, if we add a new post, we get to see it in the sidebar right away!
You can find the complete code on GitHub.
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter