Today we are going to learn how to implement the NavigationProgress component from Mantine.dev in a remix application. The component will be used to indicate that there is some asynchronous process happening (loading content, navigating, form submission).
Using <NavigationProgress>
Mantine has an additional package called @mantine/nprogress
which contains a single component and some utility functions for interacting with the component state. If you haven't installed it already follow the installation instructions first.
We are only concerned with the functions startNavigationProgress
to trigger the progress bar to start increasing and resetNavigationProgress
to reset and hide the progress bar. We will hook these up to Remix so that we start progress any time the UI is in a loading or submission state and reset progress every time the UI returns to the idle state.
The component <NavigationProgress>
by default renders a progress bar in a React Portal, this is fine if you are rendering at the top of the viewport but otherwise you may have to adjust some of the default props
Remix useTransition
hook
Remix provides a useTransition hook that exposes the current state of any loaders or actions in your application. The hook exposes quite a lot of data about the transition such as what type of transition it is, and what the next location will be, but we are only interested in the state
. When there is no transition occurring state
will be "idle"
and otherwise it will be "loading"
or "submitting"
depending on the transition type.
Implementing a <GlobalLoadingIndicator>
component
Using this knowledge we can create a small component to link Remix transitions and NavigationProgress together.
import { useEffect } from "react";
import { useTransition } from "@remix-run/react";
import {
NavigationProgress,
resetNavigationProgress,
startNavigationProgress,
} from "@mantine/nprogress";
export const GlobalLoadingIndicator = () => {
const { state } = useTransition();
useEffect(() => {
if (state === "idle") {
// we are idle so hide the progress bar
resetNavigationProgress();
} else {
// we are doing something so show the progress bar
// and allow it to increment
startNavigationProgress();
}
}, [state]);
// I set the stepInterval to 100 so it increases every 100 milliseconds
// but you can set it to something that feels nice for your app
return <NavigationProgress stepInterval={100} />;
};
Using your new component
The final step to include the progress bar in your app is to render the <GlobalLoadingIndicator>
component in your layout somewhere that it can always be seen. In my case I render it inside the <MantineProvider>
in root.tsx
.
<html lang="en">
<head>
<Meta />
<Links />
<StylesPlaceholder />
</head>
<body style={{ margin: 0 }}>
<MantineProvider
theme={{ colorScheme: "light" }}
withGlobalStyles
withNormalizeCSS
>
{/* Your usage may differ */}
<GlobalLoadingIndicator />
<Outlet />
</MantineProvider>
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
Great, now you have a basic implementation of the NavigationProgress component in your Remix application! You can play with the component props and reacting to the different transition types to improve the experience as you see fit.
Top comments (0)