Remix and data loading

Remix and data loading

Β·

3 min read

Now that we started our first Remix Project, let's see how we can make it more interactive.

In this article, we'll add loading data to our static Remix website so far.

With loading data, I'm referring to pages that get created on the fly and can eventually be powered by an external source.

Creating a post pages

We want to make a post overview with all posts we have for this article. While clicking on each item, we should be able to navigate to a single page.

Something similar to this:

  • posts/
  • posts/post-1
  • posts/post-2

Let's start by creating this dynamic post overview page.

Creating the overview page

Single, we will create multiple posts based pages. Let's create a directory in our routes folder called posts.

Inside this folder, create the index.tsx file. This will act as our overview page.

We will leverage the useLoaderData hook to load our posts, which can be used to return JSON parsed data for route loaders.

First, we want to define our posts object and load all the needed imports.

import { json } from '@remix-run/node';
import { Link, useLoaderData } from '@remix-run/react';

export const loader = async () => {
  return json({
    posts: [
      {
        slug: 'post-1',
        title: 'My First Post',
      },
      {
        slug: 'post-2',
        title: 'The second post I ever made',
      },
    ],
  });
};

Then we can export our actual rendered element which would look like this:

export default function Posts() {
  const { posts } = useLoaderData();
  return (
    <main>
      <h1>All my posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <Link to={post.slug} className='text-blue-600 underline'>
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

If we now run our project with npm run dev, we can see all the posts and even click through them.

All posts

Nice!

But before we continue, let's refactor this post array to be defined somewhere, as we want to re-use it for the single pages.

Remix recommends using a model in the models folder. Let's add a post.server.ts file in this folder.

type Post = {
  slug: string,
  title: string,
};

export async function getPosts(): Promise<Array<Post>> {
  return [
    {
      slug: 'post-1',
      title: 'My First Post',
    },
    {
      slug: 'post-2',
      title: 'The second post I ever made',
    },
  ];
}

We also upgraded to reflect the types for this post, as Remix loves TypeScript.

Then we can go back to our pages/posts/index.tsx and change the loader to look like this:

type LoaderData = {
  posts: Awaited<ReturnType<typeof getPosts>>,
};

export const loader = async () => {
  return (
    json <
    LoaderData >
    {
      posts: await getPosts(),
    }
  );
};

And by defining these types, we can also fix our render to reflect the typings.

export default function Posts() {
  const { posts } = useLoaderData() as LoaderData;
  ...
}

Perfect, Remix happy, and TypeScript satisfied.

You can find today's completed code on GitHub

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Did you find this article valuable?

Support Daily Dev Tips by becoming a sponsor. Any amount is appreciated!