Fullstack serverless web development with Remix and Architect (Part 2, nested routes)

Tuomas Koivistoinen

In part 1 of this series on Remix and Architect, we got a sample application up and running ready for development. In this part we will refactor our app to use nested routes, so that the app is easier to develop later on.

Refactoring our app to used nested routes

In the long term, we want our app to display notes and projects. For both pages, we want to have the same top bar, but for the notes page the main area should display notes, and for projects page the main area should display projects.

This means that we can have the navigation bar as a parent route, let’s call it “the app route”, and then notes and projects can be nested under it.

The app route

We start off by copying the app/routes/notes.tsx to app/routes/app.tsx. Then inside app.tsx we rename the function NotesPage to AppPage and inside it replace the main element with an <Outlet/>. This leaves only the navigation bar, and the rest of the content comes from nested routes. We also need to fix the link and can also remove unused code.

​​import { Form, Link, Outlet } from "@remix-run/react";
import { useUser } from "~/utils";

export default function AppPage() {
  const user = useUser();

  return (
    <div className="flex h-full min-h-screen flex-col">
      <header className="flex items-center justify-between bg-slate-800 p-4 text-white">
        <h1 className="text-3xl font-bold">
          <Link to="/app/notes">Notes</Link>
        </h1>
        <p>{user.email}</p>
        <Form action="/logout" method="post">
          <button
            type="submit"
            className="rounded bg-slate-600 py-2 px-4 text-blue-100 hover:bg-blue-500 active:bg-blue-600"
          >
            Logout
          </button>
        </Form>
      </header>

      <Outlet />
    </div>
  );
}

We can also fix other links by searching for them in the project and adding the app prefix, but let’s skip it and just manually type the correct paths when we encounter a 404.

The notes route

Now we can create the app/routes/app directory and copy app/routes/notes.tsx to app/routes/app/notes.tsx. This lets us nest the notes page under the app page. Then inside the new notes.tsx we can remove all but the main element from NotesPage. This leaves only the content that should come from this nested route. We can also remove unused code and imports.

import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, NavLink, Outlet, useLoaderData } from "@remix-run/react";

import { requireUserId } from "~/session.server";
import { getNoteListItems } from "~/models/note.server";

type LoaderData = {
  noteListItems: Awaited<ReturnType<typeof getNoteListItems>>;
};

export const loader: LoaderFunction = async ({ request }) => {
  const userId = await requireUserId(request);
  const noteListItems = await getNoteListItems({ userId });
  return json<LoaderData>({ noteListItems });
};

export default function NotesPage() {
  const data = useLoaderData() as LoaderData;

  return (
    <main className="flex h-full bg-white">
      <div className="h-full w-80 border-r bg-gray-50">
        <Link to="new" className="block p-4 text-xl text-blue-500">
          + New Note
        </Link>

        <hr />

        {data.noteListItems.length === 0 ? (
          <p className="p-4">No notes yet</p>
        ) : (
          <ol>
            {data.noteListItems.map((note) => (
              <li key={note.id}>
                <NavLink
                  className={({ isActive }) =>
                    `block border-b p-4 text-xl ${isActive ? "bg-white" : ""}`
                  }
                  to={note.id}
                >
                  📝 {note.title}
                </NavLink>
              </li>
            ))}
          </ol>
        )}
      </div>

      <div className="flex-1 p-6">
        <Outlet />
      </div>
    </main>
  );
}

Fix note creation and deletion

Now, you might have noticed that trying to create a note in /app/notes returns a 404 page. Lets fix it by moving the app/routes/notes directory under app/routes/app and fixing all the redirect function calls in it to use the app prefix.

Coming Up in Part 3

Now that we are using nested routing, it is easier to add child routes. When you feel ready, join me in the next part, where we will separate some of the UI code from routes into their own components.

Get in Touch.

Let’s discuss how we can help with your cloud journey. Our experts are standing by to talk about your migration, modernisation, development and skills challenges.