Bird
Raised Fist0
Remixframework~15 mins

useNavigation for pending states in Remix - Deep Dive

Choose your learning style10 modes available
Overview - useNavigation for pending states
What is it?
useNavigation is a hook in Remix that helps you track the current navigation state of your app. It tells you if the app is loading, submitting a form, or idle. This lets you show feedback like loading spinners or disable buttons while waiting for a response. It makes your app feel faster and clearer to users.
Why it matters
Without useNavigation, users might click buttons or links and not know if something is happening. This can cause confusion or repeated clicks that break things. useNavigation solves this by letting your app show a pending state, so users understand the app is working. This improves user trust and experience.
Where it fits
Before learning useNavigation, you should understand React hooks and basic Remix routing. After mastering useNavigation, you can learn advanced Remix features like data mutations, optimistic UI updates, and server actions.
Mental Model
Core Idea
useNavigation tells your app when it is waiting for a page or form action to finish, so you can show a loading or pending state.
Think of it like...
It's like a traffic light for your app's navigation: green means go (idle), yellow means wait (loading), and red means stop (submitting).
┌───────────────┐
│ useNavigation │
└──────┬────────┘
       │
       ▼
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│   idle       │◄─────►│  loading      │◄─────►│  submitting   │
└───────────────┘       └───────────────┘       └───────────────┘
Build-Up - 6 Steps
1
FoundationWhat is useNavigation Hook
🤔
Concept: Introduce the useNavigation hook and its purpose in Remix.
useNavigation is a React hook provided by Remix. It returns an object that describes the current navigation state of your app. This state can be 'idle' when nothing is happening, 'loading' when a new page or data is loading, or 'submitting' when a form is being sent.
Result
You get a simple way to know if your app is waiting for something.
Understanding that useNavigation tracks navigation states is the foundation for showing feedback during app transitions.
2
FoundationBasic Navigation States Explained
🤔
Concept: Learn the three main states useNavigation reports: idle, loading, and submitting.
The navigation state has three values: - idle: no navigation or form submission happening - loading: the app is fetching new data or a new page - submitting: a form is being sent to the server These states help you decide what UI to show.
Result
You can distinguish when your app is busy or ready.
Knowing these states helps you avoid confusing users by showing appropriate UI during waits.
3
IntermediateUsing useNavigation to Show Loading UI
🤔Before reading on: do you think useNavigation can detect both page loads and form submissions? Commit to your answer.
Concept: Learn how to use useNavigation to display a loading spinner or disable buttons during navigation or form submission.
Import useNavigation from '@remix-run/react'. Call it inside your component to get the navigation object. Check navigation.state. If it's 'loading' or 'submitting', show a spinner or disable inputs. For example: const navigation = useNavigation(); const isPending = navigation.state !== 'idle'; return (); This prevents user actions while waiting.
Result
Your UI reacts instantly to navigation changes, improving user experience.
Using useNavigation to control UI states prevents user confusion and accidental repeated actions.
4
IntermediateHandling Multiple Pending States Gracefully
🤔Before reading on: should you treat 'loading' and 'submitting' states the same or differently in UI? Commit to your answer.
Concept: Learn to differentiate UI feedback for loading new pages versus submitting forms using useNavigation.
Sometimes you want different UI for loading and submitting. For example, show a full-page spinner when loading a new page, but only disable the submit button when submitting a form. You can check navigation.state and render accordingly: if (navigation.state === 'loading') return ; if (navigation.state === 'submitting') return ; This improves clarity for users.
Result
Your app gives precise feedback matching the user's action.
Distinguishing between loading and submitting states makes your app's feedback more intuitive and less frustrating.
5
AdvancedOptimizing UX with useNavigation and Transitions
🤔Before reading on: do you think useNavigation alone can handle optimistic UI updates? Commit to your answer.
Concept: Explore how useNavigation works with Remix transitions to create smooth user experiences, including optimistic UI patterns.
useNavigation tracks navigation state but does not handle data changes itself. Combine it with Remix's transition API to show optimistic UI—immediately updating UI before server response. For example, disable inputs and show a loading indicator while the form submits, then update UI on success. This reduces perceived wait time and improves responsiveness.
Result
Users feel the app is faster and more responsive.
Understanding useNavigation's role in transitions helps build fluid, user-friendly interfaces.
6
ExpertInternal Mechanics of useNavigation State Updates
🤔Before reading on: do you think useNavigation state updates are synchronous or asynchronous with navigation events? Commit to your answer.
Concept: Dive into how Remix updates useNavigation state internally during navigation and form submissions.
Remix listens to browser navigation events and form submissions. When a navigation starts, Remix sets navigation.state to 'loading' or 'submitting' asynchronously. It tracks the lifecycle of the request and updates the state when the response arrives or errors occur. This state is stored in React context and triggers re-renders of components using useNavigation. This design ensures UI stays in sync with network activity.
Result
You understand why UI updates happen exactly when navigation state changes.
Knowing the async update mechanism prevents bugs related to stale or out-of-sync UI states.
Under the Hood
useNavigation uses React context internally to share navigation state across components. When a navigation or form submission starts, Remix updates this state asynchronously based on network events. Components using useNavigation re-render automatically to reflect the current state. This state machine has three states: idle, loading, and submitting, which correspond to no activity, page data loading, and form submission respectively.
Why designed this way?
Remix was designed to provide seamless user experiences with minimal boilerplate. Centralizing navigation state in a React context allows any component to react to navigation changes without prop drilling. The three-state model is simple yet expressive enough to cover common navigation scenarios. Alternatives like manual state management would be error-prone and verbose.
┌───────────────┐
│ Browser Event │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Remix Router  │
│ detects nav   │
└──────┬────────┘
       │ updates
       ▼
┌───────────────┐       ┌───────────────┐
│ React Context │──────▶│ useNavigation │
│ navigation   │       │ hook returns  │
│ state        │       │ current state │
└───────────────┘       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does useNavigation tell you when data fetching inside loaders is done? Commit to yes or no.
Common Belief:useNavigation tells you exactly when all data loading is finished.
Tap to reveal reality
Reality:useNavigation only tracks navigation and form submission states, not the completion of data loading inside loaders.
Why it matters:Relying on useNavigation alone to detect data readiness can cause UI to show loading states incorrectly or miss showing them.
Quick: Can useNavigation detect client-side state changes unrelated to navigation? Commit to yes or no.
Common Belief:useNavigation tracks all app state changes, including client-side updates.
Tap to reveal reality
Reality:useNavigation only tracks navigation and form submission states, not client-side state changes like toggling UI elements.
Why it matters:Misusing useNavigation for client-side state can lead to confusing UI and bugs.
Quick: Does useNavigation state update immediately when navigation starts? Commit to immediate or delayed.
Common Belief:useNavigation state updates synchronously as soon as navigation starts.
Tap to reveal reality
Reality:useNavigation state updates asynchronously after navigation events are detected and processed.
Why it matters:Expecting synchronous updates can cause race conditions or UI flicker.
Quick: Can you use useNavigation to cancel ongoing navigations? Commit to yes or no.
Common Belief:useNavigation can be used to cancel or abort navigations in progress.
Tap to reveal reality
Reality:useNavigation only reports navigation state; it does not provide control to cancel navigations.
Why it matters:Trying to cancel navigations via useNavigation leads to confusion and requires other APIs.
Expert Zone
1
useNavigation state updates are batched with React rendering, so multiple navigation events in quick succession may appear as a single state change.
2
The 'submitting' state includes both GET and POST form submissions, but you can inspect submission details for finer control.
3
useNavigation does not differentiate between client-side and server-side navigations; it treats both uniformly, which can affect how you design loading UI.
When NOT to use
Do not use useNavigation to track data loading inside loaders or fetchers; instead, use loader data or fetcher states. For client-side state changes unrelated to navigation, use React state or context. For canceling navigations, use browser APIs or Remix's navigation APIs.
Production Patterns
In production Remix apps, useNavigation is commonly used to disable form buttons during submission, show global loading indicators during page transitions, and coordinate UI feedback with server actions. It is often combined with fetcher hooks and transition states to build smooth, responsive interfaces.
Connections
React Suspense
Both manage UI feedback during asynchronous operations.
Understanding useNavigation's state model helps grasp how React Suspense handles loading states for data fetching and code splitting.
Traffic Light Systems
useNavigation's states mimic traffic light signals controlling flow.
Recognizing this pattern aids in designing clear user feedback systems that regulate user actions safely.
Event-driven Architecture
useNavigation relies on events from navigation and form submissions to update state.
Knowing event-driven principles clarifies how asynchronous UI updates are triggered and managed in Remix.
Common Pitfalls
#1Showing loading UI only after navigation completes.
Wrong approach:const navigation = useNavigation(); if (navigation.state === 'idle') { return ; } else { return null; // no loading UI }
Correct approach:const navigation = useNavigation(); if (navigation.state !== 'idle') { return ; } else { return ; }
Root cause:Misunderstanding that navigation.state changes during navigation, not after.
#2Using useNavigation to track data fetching inside loaders.
Wrong approach:const navigation = useNavigation(); if (navigation.state === 'loading') { // assume data is loading return ; }
Correct approach:const data = useLoaderData(); if (!data) { return ; }
Root cause:Confusing navigation state with data loading state.
#3Disabling buttons only on 'loading' state, ignoring 'submitting'.
Wrong approach:const navigation = useNavigation(); const isLoading = navigation.state === 'loading';
Correct approach:const navigation = useNavigation(); const isPending = navigation.state !== 'idle';
Root cause:Not accounting for form submission state causing user to click multiple times.
Key Takeaways
useNavigation is a Remix hook that tracks navigation and form submission states to help you show loading or pending UI.
It reports three states: idle, loading, and submitting, which let you tailor user feedback precisely.
Using useNavigation prevents user confusion by disabling inputs or showing spinners during waits.
It works asynchronously with Remix's routing system to keep UI in sync with network activity.
Understanding its limits and combining it with other Remix features leads to smooth, responsive apps.

Practice

(1/5)
1. What does useNavigation in Remix Framework primarily help you detect?
easy
A. When a navigation or form submission is in progress
B. When the user clicks a button
C. When the page has fully loaded
D. When the server sends a response

Solution

  1. Step 1: Understand the purpose of useNavigation

    useNavigation is a hook that tells you about the current navigation state, such as loading or submitting.
  2. Step 2: Identify what it detects

    It specifically detects when navigation or form submission is happening, so you can show loading states.
  3. Final Answer:

    When a navigation or form submission is in progress -> Option A
  4. Quick Check:

    useNavigation detects navigation state = B [OK]
Hint: useNavigation tracks loading or submitting states only [OK]
Common Mistakes:
  • Confusing useNavigation with page load completion
  • Thinking it detects button clicks directly
  • Assuming it tracks server responses
2. Which of the following is the correct way to get the navigation state using useNavigation in a Remix component?
easy
A. const navigation = useNavigation(); const state = navigation.state;
B. const navigation = useNavigation(); const state = navigation.status;
C. const navigation = useNavigation; const state = navigation.state();
D. const navigation = useNavigation(); const state = navigation.navigationState;

Solution

  1. Step 1: Recall the correct property name

    The useNavigation hook returns an object with a state property that holds the current navigation state.
  2. Step 2: Check syntax correctness

    const navigation = useNavigation(); const state = navigation.state; correctly calls useNavigation() and accesses navigation.state. Other options use wrong property names or omit parentheses.
  3. Final Answer:

    const navigation = useNavigation(); const state = navigation.state; -> Option A
  4. Quick Check:

    Correct property is navigation.state = A [OK]
Hint: Remember: useNavigation() returns object with .state property [OK]
Common Mistakes:
  • Using navigation.status instead of navigation.state
  • Forgetting parentheses after useNavigation
  • Calling navigation.state as a function
3. Given this Remix component snippet, what will be rendered when a form submission is in progress?
import { useNavigation } from '@remix-run/react';

export default function SubmitButton() {
  const navigation = useNavigation();
  return (
    <button disabled={navigation.state === 'submitting'}>
      {navigation.state === 'submitting' ? 'Submitting...' : 'Submit'}
    </button>
  );
}
medium
A. <button>Submitting...</button>
B. <button>Submit</button>
C. <button disabled>Submit</button>
D. <button disabled>Submitting...</button>

Solution

  1. Step 1: Check the condition for disabling the button

    The button is disabled when navigation.state === 'submitting'.
  2. Step 2: Check the button text during submission

    When submitting, the button text changes to 'Submitting...'. So the button is disabled and shows 'Submitting...'.
  3. Final Answer:

    <button disabled>Submitting...</button> -> Option D
  4. Quick Check:

    Disabled + 'Submitting...' text = A [OK]
Hint: Button disables and text changes when state is 'submitting' [OK]
Common Mistakes:
  • Ignoring the disabled attribute
  • Showing 'Submit' text during submission
  • Confusing 'loading' with 'submitting' state
4. Identify the error in this Remix component using useNavigation:
import { useNavigation } from '@remix-run/react';

export default function LoadingIndicator() {
  const navigation = useNavigation;
  if (navigation.state === 'loading') {
    return <div>Loading...</div>;
  }
  return <div>Ready</div>;
}
medium
A. Missing import for React
B. Forgot to call useNavigation as a function
C. Incorrect state value 'loading', should be 'submitting'
D. Return statements must be wrapped in parentheses

Solution

  1. Step 1: Check how useNavigation is used

    The code assigns const navigation = useNavigation; without parentheses, so navigation is a function reference, not the hook result.
  2. Step 2: Understand the impact

    Accessing navigation.state will cause an error because navigation is not the hook's returned object.
  3. Final Answer:

    Forgot to call useNavigation as a function -> Option B
  4. Quick Check:

    useNavigation() must be called with () = C [OK]
Hint: Always call useNavigation() with parentheses [OK]
Common Mistakes:
  • Using useNavigation without ()
  • Confusing 'loading' and 'submitting' states
  • Assuming React import is mandatory for hooks
5. You want to disable a submit button and show a spinner only when a form submission or navigation is pending using useNavigation. Which code snippet correctly implements this behavior? A)
const navigation = useNavigation();
return <button disabled={navigation.state === 'loading'}>Submit</button>;
B)
const navigation = useNavigation();
return <button disabled={navigation.state !== 'idle'}>
  {navigation.state !== 'idle' ? 'Loading...' : 'Submit'}
</button>;
C)
const navigation = useNavigation();
return <button disabled={navigation.state === 'submitting'}>
  {navigation.state === 'submitting' ? 'Submitting...' : 'Submit'}
</button>;
D)
const navigation = useNavigation();
return <button disabled={navigation.state === 'pending'}>Submit</button>;
hard
A. Disables only on 'loading' state, missing 'submitting'
B. Disables and shows submitting only during form submission
C. Disables and shows loading for any non-idle state
D. Uses invalid 'pending' state which does not exist

Solution

  1. Step 1: Understand navigation.state values

    Valid states are 'idle', 'loading', and 'submitting'. 'pending' is not valid.
  2. Step 2: Check which snippet covers all pending states

    Disables and shows loading for any non-idle state disables the button and shows loading text whenever state is not 'idle', covering both 'loading' and 'submitting'.
  3. Step 3: Evaluate other options

    A only disables on 'loading' (misses 'submitting'), B disables and shows loading for any non-idle state, C only on 'submitting', D uses invalid state.
  4. Final Answer:

    Disables and shows loading for any non-idle state -> Option C
  5. Quick Check:

    Use state !== 'idle' for all pending states = B [OK]
Hint: Check for state !== 'idle' to cover all pending states [OK]
Common Mistakes:
  • Using invalid state names like 'pending'
  • Checking only 'loading' or only 'submitting'
  • Not showing feedback for all navigation states
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard