Client setup

Configure Vite and the React, Vue or Svelte client for an Inertia.Net app.

Inertia.Net speaks the Inertia.js v3 protocol. Install a v3 client package; v2 clients are not supported.

Install the client

Run the npm commands from the ASP.NET Core project directory (the one containing your .csproj).

Text
npm install react react-dom @vitejs/plugin-react @inertiajs/react@^3
npm install -D vite typescript

The starter templates use ClientApp/pages. The manual examples below use resources/js/Pages. Keep the resolver, file paths and Vite entry consistent with the layout you choose.

Vite configuration

The same Vite config works for every client; only the framework plugin changes.

Text
// vite.config.ts
import { defineConfig, type Plugin } from 'vite'
import react from '@vitejs/plugin-react'            // Vue: @vitejs/plugin-vue, Svelte: @sveltejs/vite-plugin-svelte
import { mkdirSync, rmSync, writeFileSync } from 'node:fs'

// Writes the dev server URL to wwwroot/hot. Inertia.Net reads it in Development
// and renders the dev-server tags instead of the manifest tags.
function hotFile(): Plugin {
  const file = 'wwwroot/hot'
  return {
    name: 'inertia-net-hot-file',
    apply: 'serve',
    configureServer(server) {
      server.httpServer?.once('listening', () => {
        const address = server.httpServer!.address()
        if (address && typeof address === 'object') {
          mkdirSync('wwwroot', { recursive: true })
          writeFileSync(file, `http://localhost:${address.port}`)
        }
      })
      const clean = () => rmSync(file, { force: true })
      process.on('exit', clean)
      process.on('SIGINT', () => process.exit())
      process.on('SIGTERM', () => process.exit())
    },
  }
}

export default defineConfig(({ command }) => ({
  plugins: [react(), hotFile()],
  // Built assets live at /build/. The dev server serves from the root because
  // Inertia.Net emits {origin}/@vite/client and {origin}/{entry}.
  base: command === 'build' ? '/build/' : '/',
  server: { port: 5173, strictPort: true, origin: 'http://localhost:5173' },
  build: {
    outDir: 'wwwroot/build',
    emptyOutDir: true,
    manifest: true,
    rollupOptions: { input: 'resources/js/app.tsx' },
  },
}))

Add these fields to package.json:

Text
{ "type": "module", "scripts": { "dev": "vite", "build": "vite build" } }

The manifest key of the entry (resources/js/app.tsx) is what you pass to @vite("...").

Client entry

The optional @inertiajs/vite plugin resolves pages and mounts the app for you. With it, the entry is just:

Text
import { createInertiaApp } from '@inertiajs/react'   // or @inertiajs/vue3, @inertiajs/svelte

createInertiaApp()

Without the plugin, provide the resolver and setup yourself.

Text
// resources/js/app.tsx
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} />)
  },
})

Pages

A page file receives the props from Render(...) as normal component props.

Text
// resources/js/Pages/Users/Index.tsx
import { Link } from '@inertiajs/react'

export default function Index({ users }: { users: { id: number; name: string }[] }) {
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}><Link href={`/users/${u.id}`}>{u.name}</Link></li>
      ))}
    </ul>
  )
}

Use the generated types

Inertia.Net can write TypeScript declarations for your page props, shared props and flash data. See TypeScript types.

Development workflow

Text
npm run dev          # terminal 1: Vite on :5173, writes wwwroot/hot
dotnet watch         # terminal 2: the app, ASPNETCORE_ENVIRONMENT=Development

Open the ASP.NET Core URL, not the Vite URL. In Development the app reads wwwroot/hot and renders the Vite tags; in production it reads the manifest.

For production:

Text
npm run build
dotnet publish -c Release

Node is needed to build the assets and only needed at runtime when SSR is enabled.

Using @inertiajs/vite

The plugin also contributes the SSR dev endpoint and page resolution. Add it before the hot-file plugin:

Text
import inertia from '@inertiajs/vite'

export default defineConfig({
  plugins: [
    react(),
    inertia({ ssr: { entry: 'resources/js/ssr.tsx', host: '127.0.0.1', port: 13714 } }),
    hotFile(),
  ],
})

Keep the SSR listener on loopback: the renderer trusts its output and has no authentication. The SSR guide covers the full setup.