Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Inertia.js
https://github.com/inertiajs/inertia
Admin
Inertia.js enables the creation of modern single-page applications with React, Vue, and Svelte using
...
Tokens:
2,462
Snippets:
14
Trust Score:
7.3
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
84.8
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Inertia.js Inertia.js is a modern framework for building single-page applications (SPAs) using classic server-side routing and controllers. It allows developers to build React, Vue, and Svelte applications without the complexity of building a separate API, enabling a seamless development experience where server-side frameworks like Laravel can render client-side components directly. The framework handles page navigation, form submissions, and state management while maintaining the feel of a traditional server-rendered application. The core functionality revolves around a router that intercepts navigation events and makes XHR requests to the server, which responds with JSON containing the component name and props. The client-side adapter then renders the appropriate component with the received props, creating a smooth SPA experience. Inertia provides built-in support for form handling with validation, progress indicators, prefetching, infinite scroll, deferred props loading, and optimistic UI updates. ## createInertiaApp The `createInertiaApp` function initializes the Inertia application for both client-side rendering (CSR) and server-side rendering (SSR). It configures component resolution, sets up the progress indicator, and bootstraps the React application with the initial page data from the server. ```typescript import { createInertiaApp } from '@inertiajs/react' import { createRoot } from 'react-dom/client' createInertiaApp({ resolve: (name) => { const pages = import.meta.glob('./Pages/**/*.tsx', { eager: true }) return pages[`./Pages/${name}.tsx`] }, setup({ el, App, props }) { createRoot(el).render(<App {...props} />) }, progress: { color: '#4B5563', showSpinner: true, }, title: (title) => `${title} - My App`, defaults: { form: { recentlySuccessfulDuration: 3000, }, prefetch: { cacheFor: 30000, hoverDelay: 100, }, }, }) ``` ## router.visit The `router.visit` method navigates to a URL and renders the corresponding page component. It supports various HTTP methods, data transmission, scroll preservation, and lifecycle callbacks for handling navigation events. ```typescript import { router } from '@inertiajs/react' // Basic navigation router.visit('/users') // POST request with data router.post('/users', { name: 'John Doe', email: 'john@example.com', }, { preserveScroll: true, preserveState: true, onSuccess: (page) => { console.log('User created successfully') }, onError: (errors) => { console.log('Validation errors:', errors) }, onFinish: () => { console.log('Request completed') }, }) // PUT request with headers router.put('/users/1', { name: 'Jane Doe' }, { headers: { 'X-Custom-Header': 'value' }, only: ['user'], // Partial reload }) // DELETE request router.delete('/users/1', { onBefore: (visit) => confirm('Are you sure?'), }) ``` ## router.reload The `router.reload` method refreshes the current page data without a full navigation, useful for updating specific props or refreshing after external changes. ```typescript import { router } from '@inertiajs/react' // Full page reload router.reload() // Partial reload - only fetch specific props router.reload({ only: ['notifications', 'messages'] }) // Reload except certain props router.reload({ except: ['heavyData'] }) // Reset specific props to their initial state router.reload({ reset: ['form'] }) // With custom headers router.reload({ only: ['user'], headers: { 'Cache-Control': 'no-cache' }, onSuccess: (page) => { console.log('Data refreshed:', page.props.user) }, }) ``` ## router.prefetch The `router.prefetch` method preloads page data before navigation, improving perceived performance by having data ready when the user clicks a link. ```typescript import { router } from '@inertiajs/react' // Basic prefetch router.prefetch('/dashboard') // Prefetch with caching options router.prefetch('/users', {}, { cacheFor: '5m', // Cache for 5 minutes cacheTags: ['users'], }) // Check if data is cached const cached = router.getCached('/dashboard') if (cached) { console.log('Data is cached') } // Flush specific cache router.flush('/dashboard') // Flush by cache tags router.flushByCacheTags('users') // Flush all prefetched data router.flushAll() ``` ## useForm The `useForm` hook provides comprehensive form state management with validation, submission handling, error tracking, and support for optimistic updates and Laravel Precognition validation. ```typescript import { useForm } from '@inertiajs/react' function CreateUserForm() { const form = useForm({ name: '', email: '', role: 'user', }) const handleSubmit = (e) => { e.preventDefault() form.post('/users', { onSuccess: () => { form.reset() }, onError: (errors) => { console.log('Validation errors:', errors) }, }) } return ( <form onSubmit={handleSubmit}> <input value={form.data.name} onChange={(e) => form.setData('name', e.target.value)} /> {form.errors.name && <span>{form.errors.name}</span>} <input value={form.data.email} onChange={(e) => form.setData('email', e.target.value)} /> {form.errors.email && <span>{form.errors.email}</span>} <button type="submit" disabled={form.processing}> {form.processing ? 'Creating...' : 'Create User'} </button> {form.recentlySuccessful && <span>User created!</span>} </form> ) } // With Precognition validation function ValidatedForm() { const form = useForm('post', '/users', { name: '', email: '', }) return ( <input value={form.data.email} onChange={(e) => form.setData('email', e.target.value)} onBlur={() => form.validate('email')} /> {form.invalid('email') && <span>{form.errors.email}</span>} {form.validating && <span>Validating...</span>} ) } // With optimistic updates function OptimisticForm() { const form = useForm({ title: '' }) const handleSubmit = () => { form.optimistic((props) => ({ todos: [...props.todos, { title: form.data.title, pending: true }], })).post('/todos') } } ``` ## usePage The `usePage` hook provides access to the current page data including props, URL, component name, and flash messages from the server. ```typescript import { usePage } from '@inertiajs/react' function UserProfile() { const { props, url, component } = usePage() // Access shared props (defined in HandleInertiaRequests middleware) const { auth, flash } = props return ( <div> <h1>Welcome, {auth.user.name}</h1> <p>Current URL: {url}</p> <p>Component: {component}</p> {flash.success && ( <div className="alert-success">{flash.success}</div> )} {flash.error && ( <div className="alert-error">{flash.error}</div> )} </div> ) } // With TypeScript interface PageProps { user: { id: number; name: string; email: string } posts: Array<{ id: number; title: string }> } function TypedComponent() { const { props } = usePage<PageProps>() return ( <div> <h1>{props.user.name}</h1> {props.posts.map((post) => ( <article key={post.id}>{post.title}</article> ))} </div> ) } ``` ## Link The `Link` component creates Inertia-powered navigation links that intercept clicks and make XHR requests instead of full page loads, with support for prefetching and various HTTP methods. ```typescript import { Link } from '@inertiajs/react' function Navigation() { return ( <nav> {/* Basic link */} <Link href="/dashboard">Dashboard</Link> {/* Link with prefetch on hover */} <Link href="/users" prefetch>Users</Link> {/* Prefetch on mount */} <Link href="/settings" prefetch="mount">Settings</Link> {/* POST link */} <Link href="/logout" method="post" as="button"> Logout </Link> {/* Link with data */} <Link href="/search" data={{ query: 'inertia', page: 1 }} preserveState > Search </Link> {/* Link with callbacks */} <Link href="/users/1" onBefore={() => confirm('Navigate?')} onStart={() => console.log('Starting...')} onSuccess={(page) => console.log('Loaded:', page)} > View User </Link> {/* Partial reload link */} <Link href="/notifications" only={['notifications']}> Refresh Notifications </Link> {/* With view transitions */} <Link href="/gallery" viewTransition> Gallery </Link> {/* Instant navigation with component preload */} <Link href="/about" component="About" instant> About </Link> </nav> ) } ``` ## Form Component The `Form` component provides a declarative way to handle form submissions with built-in validation, progress tracking, and optimistic updates without needing to use the `useForm` hook. ```typescript import { Form, useFormContext } from '@inertiajs/react' function ContactForm() { return ( <Form action="/contact" method="post" resetOnSuccess disableWhileProcessing onSuccess={() => alert('Message sent!')} onError={(errors) => console.log(errors)} > {({ errors, processing, isDirty, progress }) => ( <> <input name="name" required /> {errors.name && <span>{errors.name}</span>} <input name="email" type="email" required /> {errors.email && <span>{errors.email}</span>} <textarea name="message" required /> {errors.message && <span>{errors.message}</span>} {progress && ( <progress value={progress.percentage} max="100" /> )} <button type="submit" disabled={processing}> {processing ? 'Sending...' : 'Send Message'} </button> </> )} </Form> ) } // Using useFormContext inside Form function SubmitButton() { const form = useFormContext() return ( <button type="submit" disabled={form?.processing}> {form?.processing ? 'Submitting...' : 'Submit'} </button> ) } // With Precognition validation function ValidatedContactForm() { return ( <Form action="/contact" method="post" validateFiles validationTimeout={1000} > {({ validate, invalid, errors }) => ( <input name="email" onBlur={(e) => validate('email')} className={invalid('email') ? 'error' : ''} /> )} </Form> ) } ``` ## Head The `Head` component manages document head elements like title, meta tags, and scripts, with automatic deduplication and server-side rendering support. ```typescript import { Head } from '@inertiajs/react' function UserProfile({ user }) { return ( <> <Head> <title>{user.name}'s Profile</title> <meta name="description" content={`Profile page for ${user.name}`} /> <meta property="og:title" content={user.name} /> <meta property="og:image" content={user.avatar} /> <link rel="canonical" href={`https://example.com/users/${user.id}`} /> </Head> <div> <h1>{user.name}</h1> </div> </> ) } // Simple title-only usage function Dashboard() { return ( <> <Head title="Dashboard" /> <div>Dashboard content</div> </> ) } // With head-key for deduplication function Layout({ children }) { return ( <> <Head> <meta head-key="theme-color" name="theme-color" content="#4B5563" /> </Head> {children} </> ) } ``` ## Deferred The `Deferred` component handles lazy-loaded props that are fetched after the initial page load, showing a fallback until the data is available. ```typescript import { Deferred } from '@inertiajs/react' function Dashboard({ user }) { return ( <div> <h1>Welcome, {user.name}</h1> {/* Single deferred prop */} <Deferred data="notifications" fallback={<LoadingSpinner />}> <NotificationsList /> </Deferred> {/* Multiple deferred props */} <Deferred data={['analytics', 'recentActivity']} fallback={() => <Skeleton />} > {({ reloading }) => ( <div className={reloading ? 'opacity-50' : ''}> <AnalyticsChart /> <ActivityFeed /> </div> )} </Deferred> </div> ) } // Server-side (Laravel controller) // return Inertia::render('Dashboard', [ // 'user' => $user, // 'notifications' => Inertia::defer(fn () => $user->notifications), // 'analytics' => Inertia::defer(fn () => $this->getAnalytics()), // ]); ``` ## WhenVisible The `WhenVisible` component loads data when an element becomes visible in the viewport, enabling lazy loading of content as the user scrolls. ```typescript import { WhenVisible } from '@inertiajs/react' function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> {/* Load comments when scrolled into view */} <WhenVisible data="comments" fallback={<CommentsSkeleton />} buffer={200} > <CommentsList /> </WhenVisible> {/* Always refetch when visible */} <WhenVisible data="liveStats" always fallback={<div>Loading stats...</div>} > {({ fetching }) => ( <div className={fetching ? 'updating' : ''}> <LiveStats /> </div> )} </WhenVisible> {/* With reload options */} <WhenVisible data={['recommendations', 'trending']} params={{ only: ['recommendations', 'trending'] }} buffer={100} as="section" > <RecommendationsSection /> </WhenVisible> </div> ) } ``` ## InfiniteScroll The `InfiniteScroll` component provides automatic pagination loading as the user scrolls, with support for bidirectional scrolling and manual load triggers. ```typescript import { InfiniteScroll } from '@inertiajs/react' function PostsFeed({ posts }) { return ( <InfiniteScroll data="posts" buffer={300} loading={<LoadingSpinner />} next={({ loading, fetch, hasMore }) => ( hasMore && !loading && ( <button onClick={fetch}>Load More</button> ) )} > {({ loading, loadingNext }) => ( <div className={loading ? 'opacity-50' : ''}> {posts.data.map((post) => ( <PostCard key={post.id} post={post} /> ))} {loadingNext && <LoadingSpinner />} </div> )} </InfiniteScroll> ) } // Chat-style reverse scrolling function ChatMessages({ messages }) { return ( <InfiniteScroll data="messages" reverse autoScroll previous={({ loading }) => loading && <LoadingSpinner />} > {messages.data.map((msg) => ( <Message key={msg.id} message={msg} /> ))} </InfiniteScroll> ) } // Manual mode after initial loads function ProductList({ products }) { return ( <InfiniteScroll data="products" manualAfter={3} next={({ fetch, hasMore, manualMode }) => ( manualMode && hasMore && ( <button onClick={fetch}>Load More Products</button> ) )} > <ProductGrid products={products.data} /> </InfiniteScroll> ) } ``` ## usePoll The `usePoll` hook enables automatic data refresh at specified intervals, useful for real-time data without WebSockets. ```typescript import { usePoll } from '@inertiajs/react' function LiveDashboard({ stats }) { // Poll every 5 seconds const { stop, start } = usePoll(5000, { only: ['stats'], }) return ( <div> <h1>Live Stats</h1> <StatsDisplay stats={stats} /> <button onClick={stop}>Pause Updates</button> <button onClick={start}>Resume Updates</button> </div> ) } // With keepAlive (continue polling when tab is hidden) function NotificationCounter({ count }) { usePoll(10000, { only: ['count'] }, { keepAlive: true, autoStart: true, }) return <span className="badge">{count}</span> } // Manual start function OptionalPolling({ data }) { const { start, stop } = usePoll(3000, { only: ['data'] }, { autoStart: false, }) return ( <div> <DataDisplay data={data} /> <button onClick={start}>Start Live Updates</button> </div> ) } ``` ## useRemember The `useRemember` hook persists component state in the browser history, restoring it when the user navigates back to the page. ```typescript import { useRemember } from '@inertiajs/react' function SearchPage({ results }) { // State is restored when navigating back const [filters, setFilters] = useRemember({ query: '', category: 'all', sortBy: 'date', }, 'search-filters') const [view, setView] = useRemember('grid', 'view-mode') return ( <div> <input value={filters.query} onChange={(e) => setFilters({ ...filters, query: e.target.value })} placeholder="Search..." /> <select value={filters.category} onChange={(e) => setFilters({ ...filters, category: e.target.value })} > <option value="all">All Categories</option> <option value="tech">Technology</option> <option value="design">Design</option> </select> <button onClick={() => setView(view === 'grid' ? 'list' : 'grid')}> Toggle View: {view} </button> <ResultsDisplay results={results} view={view} /> </div> ) } ``` ## router.on (Global Events) The `router.on` method registers global event listeners for all Inertia navigation events, useful for analytics, logging, or global UI updates. ```typescript import { router } from '@inertiajs/react' // Listen to navigation events const removeBeforeListener = router.on('before', (event) => { console.log('Navigating to:', event.detail.visit.url) // Return false to cancel navigation if (event.detail.visit.url.pathname === '/restricted') { return false } }) const removeSuccessListener = router.on('success', (event) => { // Track page view analytics.track('page_view', { url: event.detail.page.url, component: event.detail.page.component, }) }) const removeErrorListener = router.on('error', (event) => { // Log validation errors console.error('Validation errors:', event.detail.errors) }) const removeFinishListener = router.on('finish', (event) => { const { visit } = event.detail if (visit.completed) console.log('Navigation completed') if (visit.cancelled) console.log('Navigation cancelled') if (visit.interrupted) console.log('Navigation interrupted') }) // Listen to flash messages router.on('flash', (event) => { const { flash } = event.detail if (flash.success) toast.success(flash.success) if (flash.error) toast.error(flash.error) }) // Clean up listeners removeBeforeListener() removeSuccessListener() ``` ## useHttp The `useHttp` hook provides form-like state management for making HTTP requests that don't navigate, useful for API calls that return JSON responses without page transitions. ```typescript import { useHttp } from '@inertiajs/react' interface ApiResponse { success: boolean data: { id: number; status: string } } function StatusUpdater() { const http = useHttp<{ status: string }, ApiResponse>({ status: 'pending', }) const updateStatus = async () => { try { const response = await http.post('/api/status', { onSuccess: (data) => { console.log('Updated:', data) }, }) console.log('Response:', response) } catch (error) { console.error('Failed:', error) } } return ( <div> <select value={http.data.status} onChange={(e) => http.setData('status', e.target.value)} > <option value="pending">Pending</option> <option value="active">Active</option> <option value="completed">Completed</option> </select> <button onClick={updateStatus} disabled={http.processing}> {http.processing ? 'Updating...' : 'Update Status'} </button> {http.response && <span>Last update: {http.response.data.status}</span>} {http.errors.status && <span>{http.errors.status}</span>} </div> ) } // With optimistic updates function LikeButton({ postId, likes }) { const http = useHttp({ liked: false }) const toggleLike = () => { http.optimistic((current) => ({ liked: !current.liked, })).post(`/posts/${postId}/like`) } return ( <button onClick={toggleLike}> {http.data.liked ? 'Unlike' : 'Like'} ({likes}) </button> ) } ``` ## http Module The `http` module provides low-level HTTP client configuration, allowing custom clients and request/response interceptors for authentication, logging, or error handling. ```typescript import { http } from '@inertiajs/react' // Configure XSRF token handling http.setClient({ xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', }) // Add request interceptor for authentication http.onRequest(async (config) => { const token = await getAuthToken() return { ...config, headers: { ...config.headers, Authorization: `Bearer ${token}`, }, } }) // Add response interceptor for logging http.onResponse(async (response) => { console.log(`${response.status}: ${response.headers['x-request-id']}`) return response }) // Add error handler for token refresh http.onError(async (error) => { if (error.response?.status === 401) { await refreshAuthToken() // Retry will happen automatically } }) // Custom HTTP client (e.g., using Axios) import { axiosAdapter } from '@inertiajs/core' import axios from 'axios' const axiosInstance = axios.create({ baseURL: '/api', timeout: 10000, }) http.setClient(axiosAdapter(axiosInstance)) ``` ## config The `config` module allows customizing default Inertia behavior for forms, prefetching, and visit options across the entire application. ```typescript import { config } from '@inertiajs/react' // Set global form defaults config.set('form.recentlySuccessfulDuration', 3000) config.set('form.forceIndicesArrayFormatInFormData', true) config.set('form.withAllErrors', false) // Set prefetch defaults config.set('prefetch.cacheFor', 60000) // 1 minute config.set('prefetch.hoverDelay', 100) // 100ms // Set default visit options config.set('visitOptions', (href, options) => { return { ...options, preserveScroll: true, headers: { ...options.headers, 'X-Custom-Header': 'value', }, } }) // Get config values const cacheDuration = config.get('prefetch.cacheFor') const hoverDelay = config.get('prefetch.hoverDelay') // Set multiple values at once config.set({ 'form.recentlySuccessfulDuration': 5000, 'prefetch.cacheFor': 120000, }) ``` ## setLayoutProps The `setLayoutProps` function allows passing props from page components to their parent layouts, enabling child-to-parent communication for dynamic layout customization. ```typescript import { setLayoutProps, resetLayoutProps } from '@inertiajs/react' import { useEffect } from 'react' // In page component function ArticlePage({ article }) { useEffect(() => { setLayoutProps({ title: article.title, showSidebar: true, breadcrumbs: ['Home', 'Articles', article.title], }) return () => resetLayoutProps() }, [article]) return <article>{article.content}</article> } // For named layouts function DashboardPage({ user }) { useEffect(() => { // Props for specific named layout setLayoutProps('sidebar', { activeItem: 'dashboard', collapsed: false, }) setLayoutProps('header', { title: 'Dashboard', user: user, }) return () => resetLayoutProps() }, [user]) return <div>Dashboard content</div> } // In layout component (receives props via store) import { store } from '@inertiajs/react' function AppLayout({ children }) { const layoutProps = store.get() // { title, showSidebar, breadcrumbs } return ( <div> <header>{layoutProps.title}</header> {layoutProps.showSidebar && <Sidebar />} {children} </div> ) } ``` ## Summary Inertia.js bridges the gap between traditional server-rendered applications and modern SPAs by providing a seamless development experience. The primary use case is building full-stack applications where the server controls routing and data fetching while the client handles rendering with React, Vue, or Svelte. This approach eliminates the need for a separate API layer, reduces complexity, and enables features like server-side rendering, form validation with Laravel Precognition, and automatic CSRF protection. The framework excels at building admin panels, dashboards, and content-heavy applications where SEO is important and the development team wants to leverage existing server-side frameworks. Integration with server-side frameworks like Laravel is straightforward through the official server-side adapters. The typical pattern involves controllers returning `Inertia::render()` responses with component names and props, which the client-side adapter then renders. Forms can be submitted using the `useForm` hook or `Form` component with built-in validation error handling, progress tracking, and optimistic updates. For real-time features, the `usePoll` hook provides simple polling, while `WhenVisible` and `Deferred` components enable lazy loading of expensive data. The prefetching system improves perceived performance by loading pages before the user clicks, and the `InfiniteScroll` component handles paginated content elegantly.