Key points to get started with Remix

Apr 11, 2022

Remix is a new framework for React that will be a big trend soon. In this article I will try to focus on key points, so that you can build a mental model to start developing.

What is Remix?

Remix is a React framework focused on Server Side Rendering. This means that we will have a server that will render our HTML and serve it to the client. In that case we wouldn't need, for example, any Javascript on the client side to serve the page. The client-side Javascript will be only for what we call progressive enhancement, to improve the user experience when necessary.

Which points should I focus on?

  • Create a project;
  • Main conventions for creating a page with Remix;
  • Understand what types of prior knowledge you can take advantage of;

Is starting a project easy?

Yes! To start your project, the framework gives you several options, from adapters for each hosting you need to ready-made templates! These templates you can click here to learn more.

If you want to create a clean project, the command is simple:

npx create-remix@latest

You will configure essential things from the terminal itself.

Remix Conventions

All Remix conventions are very simple and very intuitive to understand. The main ones are:

  • Your page will be the React component exported by default with export default;
  • loader: part responsible for retrieving data from a database/APIs and returning to our route (runs only server side). In addition, it is possible to configure headers, throw errors and serve as a resource route. It works like GET.;
  • action: responsible for processing data coming from the client and sending them to the database/APIs (runs only server side). It also has extra behavior similar to loader. Any request other than a GET will call its action;
  • meta: responsible for creating meta tags for your HTML document;
  • headers: each route can define its own HTTP headers such as the Cache-Control header;
  • links: you can define per route which <link> elements to add;
  • CatchBoundary: React component that should be exported to handle some error state of your loader or action;
  • ErrorBoundary: React component that should be exported to handle any route, rendering or data loading errors. This is a different case from errors that are easily recoverable such as 404 errors.
export const loader: LoaderFunction = () => {
  // loader code
}

export const action: ActionFunction = () => {
  // action code
}

export const headers: HeadersFunction = () => {
  // return headers here
}

export const meta: MetaFunction = () => {
  // return meta tags here
}

export const links: LinksFunction = () => {
  // return <link>'s here
}

// https://remix.run/docs/en/v1/api/conventions#catchboundary
export const CatchBoundary = () => {
  const caught = useCatch();

  return (
    <div>
      <h1>Caught</h1>
      <p>Status: {caught.status}</p>
      <pre>
        <code>{JSON.stringify(caught.data, null, 2)}</code>
      </pre>
    </div>
  )
}

// https://remix.run/docs/en/v1/api/conventions#errorboundary
export const ErrorBoundary = ({ error }) => {
  return (
    <div>
      <h1>Error</h1>
      <p>{error.message}</p>
      <p>The stack trace is:</p>
      <pre>{error.stack}</pre>
    </div>
  )
}

const YourPage = () => {
  // hook used to access loader data
  // exported by remix package
  const data = useLoaderData()

  return <div>Your page</div>
}

export default YourPage

To avoid going into too much detail in this article about each of these points, you can check it out in more detail here.

How to take advantage of previous knowledge?

If you're from the backend scene, or even had a backend using for example express, you're going to have a pretty easy time with Remix.

As explained earlier, loader and action work as HTTP routes that your own page will access. The Remix team made both functions as simple as an HTTP route should be.

I'll exemplify using a simple express code. Imagine you have a route of users:

// create rotue
app.get('/users', async (req, res) => {
  // acess database and return users
  const users = await db.getUsers()

 // header config
  res.set('Cache-Control', 'public, max-age=300, s-maxage=600')

  if (users.length === 0) {
    // throw http error
    return res.status(404).send('No users found')
  }

  // return to endpoint
  return res.send(users)
})

The transcription for a loader in Remix is almost identical:

import type { LoaderFunction } from '@remix-run/node'
import { json } from '@remix-run/node'
// every file with .server extension will be served only
// on server side, excluding code from client build
import db from '~/utils/db.server'

export const loader: LoaderFunction = async () => {
   // acess database and return users
  const users = await db.getUsers()

  // header config
  const headers = {
    'Cache-Control': 'public, max-age=300, s-maxage=600',
  }

  if (users.length === 0) {
    // throw json error
    throw json('No users found', { headers })
  }

  // loader return
  return json(users, { headers })
}

And look how cool, if we access the URL right now, without creating a component for the route, we will notice that the route works as an API route! This can be useful in some cases.

How is it done to access the loader data inside our component? Simple! Remix exports a hook called useLoaderData.

import type { LoaderFunction } from '@remix-run/node'
import { json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import db from '~/utils/db.server'

export const loader: LoaderFunction = async () => {
  const users = await db.getUsers()

  const headers = {
    'Cache-Control': 'public, max-age=300, s-maxage=600',
  }

  if (users.length === 0) {
    throw json('No users found', { headers })
  }

  return json(users, { headers })
}

const UserTestPage = () => {
  // acess loader data
  const users = useLoaderData()

  return (
    <ul>
      {users.map((user) => (
        <li key={user.name}>{user.name}</li>
      ))}
    </ul>
  )
}

export default UserTestPage

Read about handling loader errors with CatchBoundary

I wanted to bring an example for the loader in this article. The actions are not very different. You can see an example by clicking here.

Conclusion

Remix brings out very general strengths of web fundamentals. Therefore, much of the knowledge we already have about the web can be reused in a simple way with Remix.