Hopp til hovedinnhold

Want to be really, really type safe, both on the front and back ends? With Remix, that's the default!

Remix has become one of the very best frameworks for developing full-stack web applications in React. It's quick, easy to set up, and is based off of web standards.It's just a delight.

Today I'll show you a neat trick to keep type safety between your data loaders and your frontend code!

First, if you're not familiar with Remix' way of loading data, this is how you fetch data on the backend, and serve it up to the frontend:

/* This is only run on the backend */
export async function loader({ req, res}: LoaderArgs) {
  const posts = await api.getPosts();
  return { posts };
}

export default function PostsPage() {
  // This returns the data from the `loader` function
  const { posts } = useLoader()
  return (
    <main>
      <h1>Posts</h1>
      {posts.length === 0 && <p>No posts yet</p>}
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
          <p>
            <a href={`/posts/${post.slug}`}>Read more</a>
          </p>
        </article>
      ))}
    </main>
  );
}

This works well in a frivolous JavaScript world, but with TypeScript, this will generate more red lines than a New York City subway map. So let's fix that - with a single line of code.

/** This stays the same 
 * (given that the API function returns a typed response)
 */
export async function loader({ req, res}: LoaderArgs) {
  const posts = await api.getPosts();
  return { posts };
}

export default function PostsPage() {
  // Just add `<typeof loader>` here, and you're good!
  const { posts } = useLoader<typeof loader>()
  return (
    <main>
      <h1>Posts</h1>
      {posts.length === 0 && <p>No posts yet</p>}
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
          <p>
            <a href={`/posts/${post.slug}`}>Read more</a>
          </p>
        </article>
      ))}
    </main>
  );
}

See that typeof loader type argument to the useLoader hook? That will give us the correct type definition to the back end. So if you change what your API function returns, this will automatically fail in your frontend code.

A super quick tip, that revolutionized how I write my code.

For even better runtime type safety, make sure to add a parsing step for your API layer. I'm a huge fan of zag, which you'll read more about in a later post.

Did you like the post?

Feel free to share it with friends and colleagues