Inertia.js Progress Bar: Boost Perceived Performance with a Single Line
Add instant visual feedback during page transitions in Inertia.js apps by enabling the built‑in progress bar. Learn why it matters, how to configure it, and when a custom solution might be better.
09 Jan 2026, 16:03 UTC

Problem: Blank Screens and Perceived Slowness
When an Inertia.js app navigates between pages, the new view is fetched from the server and rendered on the client. During that brief interval the browser shows a blank or flickering screen. Users often interpret this as a lagging or broken application, even if the actual load time is only a few hundred milliseconds.
Modern web users expect instant feedback. A visual cue that something is happening can reduce uncertainty, improve trust, and make the same technical performance feel faster.
Thesis: One‑Line Progress Bar, Big UX Impact
Inertia.js ships a lightweight progress bar that listens to its internal loading events. With a single import and a configuration call you get a smooth, auto‑hiding bar that appears at the top of the viewport during navigation. This low‑cost addition aligns with the UX patterns of popular frameworks like Nuxt, Next, and Remix.
1. Why a Progress Bar Matters
- Perceived Performance: Users judge speed by visual continuity. A bar signals that the app is working.
- Reduced Cognitive Load: Knowing that a transition is in progress frees mental resources for other tasks.
- Consistent UX: The bar mirrors the loading indicators in mobile apps and large SPAs, creating a familiar feel.
2. The Progress Bar API
First, install the package:
npm install @inertiajs/progress
Then import it in your root component (e.g., app.js or app.tsx):
import InertiaProgress from '@inertiajs/progress'
// Call once, typically during app initialization
InertiaProgress.init({
color: '#4B5563', // Tailwind gray-700
height: '3px', // CSS height value
// Optional: delay before showing to avoid flicker on fast loads
delay: 200,
})
Key options:
color: Hex or CSS color string.height: CSS height (e.g., "3px", "0.25rem").delay: Milliseconds before the bar appears.throttle: Minimum time between starts and finishes to avoid rapid flicker.
The bar automatically listens to Inertia’s visit events: start, finish, abort. No further code is required.
3. Customizing Appearance
The library injects a div with the class inertia-progress. You can override its styles with CSS or a preprocessor. Example using Tailwind:
.inertia-progress {
@apply bg-gray-700;
}
For brand‑specific colors, create a CSS variable and reference it:
:root { --inertia-progress-color: #FF5733; }
.inertia-progress { background-color: var(--inertia-progress-color); }
Because the bar sits at position: fixed; top: 0;, it won’t interfere with the page layout. If you use a CSS framework that already defines a top bar, adjust z-index to ensure visibility.
4. Integration with Existing CSS
When your global styles contain high‑specificity rules, the default .inertia-progress may be hidden. Inspect the DOM in dev tools and look for the inertia-progress element. If it’s not visible, add a more specific selector or increase z-index:
.app-root .inertia-progress {
z-index: 9999;
}
Responsive design is unaffected because the bar’s width is 100% of the viewport.
Worked Example: Minimal Laravel + Inertia Project
- Create a fresh Laravel + Inertia skeleton:
laravel new inertia-demo cd inertia-demo composer require inertiajs/inertia-laravel php artisan inertia:install react npm install npm run dev - Install the progress package:
npm install @inertiajs/progress - Edit
resources/js/app.js:import { createApp, h } from 'vue' import { App, plugin } from '@inertiajs/vue3' import InertiaProgress from '@inertiajs/progress' const app = createApp({ render: () => h(App, { initialPage: JSON.parse(appMeta.initialPage), resolveComponent }) }) app.use(plugin) // Enable the bar InertiaProgress.init({ color: '#4B5563', height: '3px', delay: 200 }) app.mount('#app') - Run the dev server and click between two routes. You should see a slim gray bar appear at the top, stay for the duration of the request, and then disappear.
Trade‑Off: Opinionated vs. Custom
The built‑in bar is fast to deploy but limited:
- Animation is simple linear progress; no advanced easing or custom shapes.
- Styling is constrained to the
inertia-progressclass; deep theming requires overriding the entire component. - It’s tied to Inertia’s internal events; if you replace or extend those events, the bar may not trigger.
If your brand demands a unique loading animation, or you need to coordinate the bar with other UI elements (e.g., a global spinner), building a custom component that listens to Inertia’s visit events via Inertia.on('visit', callback) gives you full control.
Actionable Takeaway
For most Inertia.js projects, enable the progress bar with the single init call. It gives users immediate feedback, improves perceived speed, and requires no additional build steps. Verify by inspecting the .inertia-progress element and ensuring it matches your CSS. If you hit styling limits or need complex animation, replace it with a custom component that hooks into Inertia’s events.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.