DEV Community

Cover image for Remix prefetch: Fetch data ahead of time
Ishan Manandhar
Ishan Manandhar

Posted on • Updated on

Remix prefetch: Fetch data ahead of time

Remix is an edge-first server-side rendering JavaScript framework built on React that allows us to build full-stack web applications with the capabilities of SSR (server-side rendering) and frontend. On the frontend, it acts as a higher level React framework that offers SSR (server-side rendering), file-based routing, nested routes and a loaders, prefetch, optimistic UI and more. It renders the data on the server and sends the html to client side as a response.

What are we building

In this article we will be exploring out an interesting feature Remix provides us with data prefetching. Although it's dead simple easy to implement this feature inside remix application this enhances UX and optimize the application performance a lot. When a user focuses or mouses-over a link, it's likely they want to go there. So we will prefetch the data before they're going to the page.

We will consume data from rickandmortyapi where we will fetch all the characters from rickymorty and implement prefetch when the user tries to visit the link (before going to individual detail page) with a hover we will fetch data ahead of time.

Creating our project

To create a new Remix app, all we have to do is start by running the below command in our terminal.

npx create-remix@latest
Enter fullscreen mode Exit fullscreen mode

This will creates a boilerplate remix application for us to start with and ask us few questions on the dev environment we want our project to set-up.

We will answer all the questions as below

# Answer the basic questions

**Where would you like to create your app?** remix-prefetch
**What type of app do you want to create?** Just the basics
**Where do you want to deploy? Choose Remix if you're unsure; it's easy to change deployment targets**. Remix App Server
**Do you want me to run `npm install`?** Yes
**TypeScript or JavaScript?** TypeScript

# changing to project directory

cd remix-prefetch

# run the application

npm run dev
Enter fullscreen mode Exit fullscreen mode

Until this point we are all done with out setup. Lets start creating an interface for our application and we can populate some data to take a look.

Adding a Layout

Before we begin i will add up a layout component so that we can wrap all of our application with this component. This acts something like a provider wrapper for our entire application.

//components/layout.tsx
const Layout = ({ children }: HTMLBodyElement) => {
  return (
    <>
      <div style={{ maxWidth: '900px', margin: '0 auto' }}>
        <h1>Rick and Morty App</h1>
        <br />
        <div>{children}</div>
      </div>
    </>
  );
};

export default Layout;

Enter fullscreen mode Exit fullscreen mode

Homepage

We definitely need some data to showed in our UI. For that we will be using an open public API from rickyandmortyapi. This will provide us with all the characters from the movie.

This is achievable using a new asynchronous function called loader that runs on the server and is solely responsible for prefetching data before the component is rendered on the server. The loader function is accompanied by a new hook called useLoaderData that can be utilized inside of our component to get access to the loaded data that is returned by the loader function after the data has been fetched.

/app/routes/index.tsx
export const loader: LoaderFunction = async () => {
  const fetchData = await fetch('https://rickandmortyapi.com/api/character/');
  const response = await fetchData.json();
  const data = response.results;
  return data;
};

Enter fullscreen mode Exit fullscreen mode

After setting up a loader, we can access whatever data is returned by this loader using the useLoaderData hook in our component.

Before we move ahead to list the data in our UI, we need to add a typescript interface just to tell typescript what type of interface type we are expecting.

export interface Characters {
  id: number;
  name: string;
  status: string;
  species: string;
  type: string;
  gender: string;
  origin: Location;
  location: Location;
  image: string;
  episode: string[];
  url: string;
  created: Date;
}

interface Location {
  name: string;
  url: string;
}
Enter fullscreen mode Exit fullscreen mode

We can now consume this data inside of our component.

export default function Index() {
  const dataList = useLoaderData();
  return (
    <Layout>
      <div>
        {dataList.map((character: Characters) => (
          <div
            key={character.id}
            style={{
              marginBottom: '30px',
              border: '1px solid #e7e7e7',
              padding: '20px',
            }}
          >
            <Link
              style={{ textDecoration: 'none' }}
              to={character.id}
            >
              <h3> {character.name}</h3>
              <div style={{ display: 'flex' }}>
                <img src={character.image} alt={character.name} />
                <ul style={{ listStyle: 'none' }}>
                  <li style={{ marginBottom: '5px' }}>
                    Species: {character.species}
                  </li>
                  <li style={{ marginBottom: '5px' }}>
                    Status : {character.status}
                  </li>
                  <li style={{ marginBottom: '5px' }}>
                    Gender: {character.gender}
                  </li>
                </ul>
              </div>
            </Link>
          </div>
        ))}
      </div>
    </Layout>
  );
}

Enter fullscreen mode Exit fullscreen mode

With all that in place you may also have noticed we have used a link feature from Remix where we added link to a dynamic page of detail screen to={character.id} lets go ahead and create our detail page.

Detailpage

To fetch the data for the requested detail page we can see the documentation on how we need to request for specific character with ID. So, it would look like

https://rickandmortyapi.com/api/character/{id}
Enter fullscreen mode Exit fullscreen mode

In order to grab the id clicked from the list of cartoon characters we can make the use of params property and pass that in inside of our Loader and make a request to the API.

export const loader: LoaderFunction = async ({ params }) => {
  const fetchData = await fetch(
    `https://rickandmortyapi.com/api/character/${params.characterId}`
  );
  const response = await fetchData.json();
  return response;
};
Enter fullscreen mode Exit fullscreen mode

We can now take the data returned from the Loader and populate it inside of our page with useLoaderData hook.

export default function Index() {
  const characterDetail: Characters = useLoaderData();
  return (
    <Layout>
      <div>
        <div>
          <Link to="/">go back</Link>
        </div>
        <br />
        <img src={characterDetail.image} alt="" />
        <h1>{characterDetail.name}</h1>
        <ul style={{ listStyle: 'none', paddingLeft: '0px' }}>
          <li style={{ marginBottom: '10px' }}>
            Species: {characterDetail.species}
          </li>
          <li style={{ marginBottom: '10px' }}>
            Status : {characterDetail.status}
          </li>
          <li style={{ marginBottom: '10px' }}>
            Gender: {characterDetail.gender}
          </li>
        </ul>
      </div>
    </Layout>
  );
}
Enter fullscreen mode Exit fullscreen mode

So, until now we have prepared all things needed to implement data prefetching. As said this is dead simple and just takes one line of prop achieve this. We will add a prop to our link component.

Adding prefetch

<Link
style={{ textDecoration: 'none' }}
to={character.id}
prefetch="intent"
>Content inside a link wrapper</Link>
Enter fullscreen mode Exit fullscreen mode

The main advantage of this is this eliminates about 1-2seconds of latency delay to fetch data from our server. Along with the subtle benefits like respecting HTTP cache headers, doing the work in browser idle time, using a different thread than your app and more. Link can automatically prefetch all the resources the next page needs: JavaScript modules, stylesheets, and data. This prop controls if and when that happens.

We can pass three different options to prefetch.

  • "none"

Default behavior. This will prevent any prefetching from happening. This is recommended when linking to pages that require a user session that the browser won't be able to prefetch anyway.

  • "intent"

Recommended if you want to prefetch. Fetches when Remix thinks the user intends to visit the link. Right now the behavior is simple: if they hover or focus the link it will prefetch the resources. In the future we hope to make this even smarter. Links with large click areas/padding get a bit of a head start. It is worth noting that when using prefetch="intent", elements will be inserted on hover/focus and removed if the loses hover/focus. Without proper cache-control headers on your loaders this could result in repeated prefetch loads if a user continually hovers on and off a link.

  • "render"

Fetches when the link is rendered.

This is taken from official documentation of Remix. You can find it here. You can also prefetch all the assets like data, modules, css before time please lean more about it here.

Remix uses browser cache under the hood for prefetching HTML which is really cool.

Conclusion

Remix is an awesome framework once you get inside it the more you love it. If you’re wondering whether or not you should try Remix for your next project? Yes please go ahead with it, its an awesome developer experience(DX).

You can also achieve this using React-query Prefetching feature inside of React application. See more here.

You can find the code for this in the Github repository here.

Happy Coding!

Discussion (0)