Pending UI
On this page

Pending UI

Introduction

When the user navigates to a new route, or submits data to an action, the UI should immediately respond to the user's actions with a pending or optimistic state. Application code is responsible for this.

Global Pending Navigation

When the user navigates to a new url, the loaders for the next page are awaited before the next page renders. You can get the pending state from useNavigation.

import { useNavigation } from "react-router";  export default function Root() {  const navigation = useNavigation();  const isNavigating = Boolean(navigation.location);   return (  <html>  <body>  {isNavigating && <GlobalSpinner />}  <Outlet />  </body>  </html>  ); } 

Local Pending Navigation

Pending indicators can also be localized to the link. NavLink's children, className, and style props can be functions that receive the pending state.

import { NavLink } from "react-router";  function Navbar() {  return (  <nav>  <NavLink to="/home">  {({ isPending }) => (  <span>Home {isPending && <Spinner />}</span>  )}  </NavLink>  <NavLink  to="/about"  style={({ isPending }) => ({  color: isPending ? "gray" : "black",  })}  >  About  </NavLink>  </nav>  ); } 

Pending Form Submission

When a form is submitted, the UI should immediately respond to the user's actions with a pending state. This is easiest to do with a fetcher form because it has its own independent state (whereas normal forms cause a global navigation).

import { useFetcher } from "react-router";  function NewProjectForm() {  const fetcher = useFetcher();   return (  <fetcher.Form method="post">  <input type="text" name="title" />  <button type="submit">  {fetcher.state !== "idle"  ? "Submitting..."  : "Submit"}  </button>  </fetcher.Form>  ); } 

For non-fetcher form submissions, pending states are available on useNavigation.

import { useNavigation, Form } from "react-router";  function NewProjectForm() {  const navigation = useNavigation();   return (  <Form method="post" action="/projects/new">  <input type="text" name="title" />  <button type="submit">  {navigation.formAction === "/projects/new"  ? "Submitting..."  : "Submit"}  </button>  </Form>  ); } 

Optimistic UI

When the future state of the UI is known by the form submission data, an optimistic UI can be implemented for instant UX.

function Task({ task }) {  const fetcher = useFetcher();   let isComplete = task.status === "complete";  if (fetcher.formData) {  isComplete =  fetcher.formData.get("status") === "complete";  }   return (  <div>  <div>{task.title}</div>  <fetcher.Form method="post">  <button  name="status"  value={isComplete ? "incomplete" : "complete"}  >  {isComplete ? "Mark Incomplete" : "Mark Complete"}  </button>  </fetcher.Form>  </div>  ); } 

Next: Testing

Docs and examples CC 4.0
Edit