Search code examples
reactjsremix.run

How to handle loading times when a user clicks a Link or NavLink in remix run


I am building an app with remix run and using nested components. When you click a NavLink or Link that loads a component that has a loader function to load data from an api, it can be very slow to get the response and render to the user. Ideally I would like the URL in the browser to change immediately on click and to load an animation while the component is loading. I know how I could implement the loading animation with react and the useEffect hook, however I'm not sure how you'd do this with remix and the Link/NavLink tags.


Solution

  • Remix tries to emulate the browser behaviour when navigating, so it doesn't change the URL until the loader has resolved.

    However, you can improve the UX by showing some loading UI with useNavigation.

    export default function App() {
      const navigation = useNavigation();
      return (
        <html lang="en">
          <head>
            <Meta />
            <Links />
          </head>
          <body>
            {navigation.state !== "idle" ? <div>Loading...</div> : null}
            <Outlet />
            <ScrollRestoration />
            <Scripts />
            <LiveReload />
          </body>
        </html>
      );
    }
    

    If the data in your loader in extremely slow and you're unable to speed it up, you might want to show fallback UI such as a skeleton which can be done with defer.

    export function loader({ params }: LoaderArgs) {
      return defer({
        // NOTE: `getFoo` isn't awaited
        foo: getFoo()
      });
    }
    
    export default function Component() {
      const data = useLoaderData<typeof loader>();
      return (
        <main>
          <h1>Foo</h1>
          <Suspense fallback={<div>Skeleton UI...</div>}>
            <Await
              resolve={data.foo}
              errorElement={
                <div>Error loading foo</div>
              }
            >
              {(foo) => (
                <div>
                  {JSON.stringify(foo, null, 2)}
                </div>
              )}
            </Await>
          </Suspense>
        </main>
      );
    }