home / skills / openagentsinc / openagents / inertia-react-development

This skill helps you build Inertia.js v2 React applications, enabling pages, forms, and navigation with deferred props, prefetching, and polling.

npx playbooks add skill openagentsinc/openagents --skill inertia-react-development

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.6 KB
---
name: inertia-react-development
description: "Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation."
license: MIT
metadata:
  author: laravel
---

# Inertia React Development

## When to Apply

Activate this skill when:

- Creating or modifying React page components for Inertia
- Working with forms in React (using `<Form>` or `useForm`)
- Implementing client-side navigation with `<Link>` or `router`
- Using v2 features: deferred props, prefetching, or polling
- Building React-specific features with the Inertia protocol

## Documentation

Use `search-docs` for detailed Inertia v2 React patterns and documentation.

## Basic Usage

### Page Components Location

React page components should be placed in the `resources/js/pages` directory.

### Page Component Structure

<!-- Basic React Page Component -->
```react
export default function UsersIndex({ users }) {
    return (
        <div>
            <h1>Users</h1>
            <ul>
                {users.map(user => <li key={user.id}>{user.name}</li>)}
            </ul>
        </div>
    )
}
```

## Client-Side Navigation

### Basic Link Component

Use `<Link>` for client-side navigation instead of traditional `<a>` tags:

<!-- Inertia React Navigation -->
```react
import { Link, router } from '@inertiajs/react'

<Link href="/">Home</Link>
<Link href="/users">Users</Link>
<Link href={`/users/${user.id}`}>View User</Link>
```

### Link with Method

<!-- Link with POST Method -->
```react
import { Link } from '@inertiajs/react'

<Link href="/logout" method="post" as="button">
    Logout
</Link>
```

### Prefetching

Prefetch pages to improve perceived performance:

<!-- Prefetch on Hover -->
```react
import { Link } from '@inertiajs/react'

<Link href="/users" prefetch>
    Users
</Link>
```

### Programmatic Navigation

<!-- Router Visit -->
```react
import { router } from '@inertiajs/react'

function handleClick() {
    router.visit('/users')
}

// Or with options
router.visit('/users', {
    method: 'post',
    data: { name: 'John' },
    onSuccess: () => console.log('Success!'),
})
```

## Form Handling

### Form Component (Recommended)

The recommended way to build forms is with the `<Form>` component:

<!-- Form Component Example -->
```react
import { Form } from '@inertiajs/react'

export default function CreateUser() {
    return (
        <Form action="/users" method="post">
            {({ errors, processing, wasSuccessful }) => (
                <>
                    <input type="text" name="name" />
                    {errors.name && <div>{errors.name}</div>}

                    <input type="email" name="email" />
                    {errors.email && <div>{errors.email}</div>}

                    <button type="submit" disabled={processing}>
                        {processing ? 'Creating...' : 'Create User'}
                    </button>

                    {wasSuccessful && <div>User created!</div>}
                </>
            )}
        </Form>
    )
}
```

### Form Component With All Props

<!-- Form Component Full Example -->
```react
import { Form } from '@inertiajs/react'

<Form action="/users" method="post">
    {({
        errors,
        hasErrors,
        processing,
        progress,
        wasSuccessful,
        recentlySuccessful,
        clearErrors,
        resetAndClearErrors,
        defaults,
        isDirty,
        reset,
        submit
    }) => (
        <>
            <input type="text" name="name" defaultValue={defaults.name} />
            {errors.name && <div>{errors.name}</div>}

            <button type="submit" disabled={processing}>
                {processing ? 'Saving...' : 'Save'}
            </button>

            {progress && (
                <progress value={progress.percentage} max="100">
                    {progress.percentage}%
                </progress>
            )}

            {wasSuccessful && <div>Saved!</div>}
        </>
    )}
</Form>
```

### Form Component Reset Props

The `<Form>` component supports automatic resetting:

- `resetOnError` - Reset form data when the request fails
- `resetOnSuccess` - Reset form data when the request succeeds
- `setDefaultsOnSuccess` - Update default values on success

Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.

<!-- Form with Reset Props -->
```react
import { Form } from '@inertiajs/react'

<Form
    action="/users"
    method="post"
    resetOnSuccess
    setDefaultsOnSuccess
>
    {({ errors, processing, wasSuccessful }) => (
        <>
            <input type="text" name="name" />
            {errors.name && <div>{errors.name}</div>}

            <button type="submit" disabled={processing}>
                Submit
            </button>
        </>
    )}
</Form>
```

Forms can also be built using the `useForm` helper for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.

### `useForm` Hook

For more programmatic control or to follow existing conventions, use the `useForm` hook:

<!-- useForm Hook Example -->
```react
import { useForm } from '@inertiajs/react'

export default function CreateUser() {
    const { data, setData, post, processing, errors, reset } = useForm({
        name: '',
        email: '',
        password: '',
    })

    function submit(e) {
        e.preventDefault()
        post('/users', {
            onSuccess: () => reset('password'),
        })
    }

    return (
        <form onSubmit={submit}>
            <input
                type="text"
                value={data.name}
                onChange={e => setData('name', e.target.value)}
            />
            {errors.name && <div>{errors.name}</div>}

            <input
                type="email"
                value={data.email}
                onChange={e => setData('email', e.target.value)}
            />
            {errors.email && <div>{errors.email}</div>}

            <input
                type="password"
                value={data.password}
                onChange={e => setData('password', e.target.value)}
            />
            {errors.password && <div>{errors.password}</div>}

            <button type="submit" disabled={processing}>
                Create User
            </button>
        </form>
    )
}
```

## Inertia v2 Features

### Deferred Props

Use deferred props to load data after initial page render:

<!-- Deferred Props with Empty State -->
```react
export default function UsersIndex({ users }) {
    // users will be undefined initially, then populated
    return (
        <div>
            <h1>Users</h1>
            {!users ? (
                <div className="animate-pulse">
                    <div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
                    <div className="h-4 bg-gray-200 rounded w-1/2"></div>
                </div>
            ) : (
                <ul>
                    {users.map(user => (
                        <li key={user.id}>{user.name}</li>
                    ))}
                </ul>
            )}
        </div>
    )
}
```

### Polling

Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.

<!-- Basic Polling -->
```react
import { usePoll } from '@inertiajs/react'

export default function Dashboard({ stats }) {
    usePoll(5000)

    return (
        <div>
            <h1>Dashboard</h1>
            <div>Active Users: {stats.activeUsers}</div>
        </div>
    )
}
```

<!-- Polling With Request Options and Manual Control -->
```react
import { usePoll } from '@inertiajs/react'

export default function Dashboard({ stats }) {
    const { start, stop } = usePoll(5000, {
        only: ['stats'],
        onStart() {
            console.log('Polling request started')
        },
        onFinish() {
            console.log('Polling request finished')
        },
    }, {
        autoStart: false,
        keepAlive: true,
    })

    return (
        <div>
            <h1>Dashboard</h1>
            <div>Active Users: {stats.activeUsers}</div>
            <button onClick={start}>Start Polling</button>
            <button onClick={stop}>Stop Polling</button>
        </div>
    )
}
```

- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive

### WhenVisible (Infinite Scroll)

Load more data when user scrolls to a specific element:

<!-- Infinite Scroll with WhenVisible -->
```react
import { WhenVisible } from '@inertiajs/react'

export default function UsersList({ users }) {
    return (
        <div>
            {users.data.map(user => (
                <div key={user.id}>{user.name}</div>
            ))}

            {users.next_page_url && (
                <WhenVisible
                    data="users"
                    params={{ page: users.current_page + 1 }}
                    fallback={<div>Loading more...</div>}
                />
            )}
        </div>
    )
}
```

## Common Pitfalls

- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
- Forgetting to add loading states (skeleton screens) when using deferred props
- Not handling the `undefined` state of deferred props before data loads
- Using `<form>` without preventing default submission (use `<Form>` component or `e.preventDefault()`)
- Forgetting to check if `<Form>` component is available in your Inertia version

Overview

This skill develops Inertia.js v2 React client applications, focusing on pages, navigation, forms, and client-side patterns. It activates when you work with <Link>, <Form>, useForm, router visits, deferred props, prefetching, polling, or other Inertia React features. The guidance optimizes SPA behavior, performance patterns, and common pitfalls.

How this skill works

The skill inspects React page structure and recommends Inertia-specific primitives: <Link> for client navigation, <Form> or useForm for submissions, router.visit for programmatic navigation, and hooks like usePoll and useForm for polling and state. It also checks for v2 features such as deferred props, prefetch flags, WhenVisible infinite scroll, and form reset behaviors to suggest loading states and error handling.

When to use it

  • Creating or updating React page components under resources/js/pages
  • Implementing client-side navigation or replacing <a> with <Link>
  • Building forms using <Form> or using the useForm hook for programmatic control
  • Adding Inertia v2 features: deferred props, prefetching, polling, or WhenVisible infinite scroll
  • Implementing programmatic visits with router.visit or method-based Link submissions

Best practices

  • Always use <Link> for internal navigation to preserve SPA behavior and preserve scroll/state
  • Show loading skeletons or placeholders when using deferred props; handle undefined data explicitly
  • Prefer <Form> for simple forms and useForm for complex programmatic flows (reset, setData, error handling)
  • Use prefetch on links for perceived performance and restrict heavy prefetching to key routes
  • When polling, use usePoll with appropriate interval, consider autoStart and keepAlive for tab behavior
  • Prevent default native form submission when not using Inertia <Form>; ensure version compatibility for component availability

Example use cases

  • User index page with deferred users prop showing skeleton while data loads
  • Create/edit user form built with <Form> to leverage processing, errors, and reset behaviors
  • Programmatic navigation after an action using router.visit with onSuccess callbacks
  • Prefetching key dashboard pages via <Link prefetch> to reduce perceived latency
  • Dashboard auto-refresh using usePoll with manual start/stop and throttling when tab is inactive

FAQ

When should I use <Form> versus useForm?

Use <Form> for simple declarative forms that need built-in processing and reset behavior. Use useForm when you need programmatic control over data, partial resets, or custom submit flows.

How do I handle deferred props in components?

Render a skeleton or fallback UI when the prop is undefined, then render the real data once it arrives. Always guard against undefined to avoid runtime errors.