\n
\n\n\n```\n\nThis template matches Inertia's script-element bootstrap. Be sure your client setup enables `future.useScriptElementForInitialPage`.\n\nThe `{{ vite() }}` function automatically includes:\n- React Fast Refresh scripts (dev mode)\n- Vite client scripts (dev mode)\n- Built CSS and JS files (production mode)\n\n### Using View Data for SEO\n\nFor dynamic page titles and meta tags, use [view data](/guides/view-data/):\n\n```html\n\n\n\n \n \n {% if page_title %}{{ page_title }}{% else %}My App{% endif %}\n {% if meta_description %}\n \n {% endif %}\n {{ vite() | safe }}\n\n\n \n
\n\n\n```\n\n## Step 6: Create Your First Page\n\nCreate `frontend/pages/Home.tsx`:\n\n```tsx\nimport { Link } from '@inertiajs/react'\n\ninterface HomeProps {\n message: string\n}\n\nexport default function Home({ message }: HomeProps) {\n return (\n
\n

Welcome

\n

{message}

\n Go to About\n
\n )\n}\n```\n\n## Step 7: Create a Layout Component\n\nCreate `frontend/components/Layout.tsx`:\n\n```tsx\nimport { Link, usePage } from '@inertiajs/react'\nimport React from 'react'\n\ninterface LayoutProps {\n children: React.ReactNode\n title?: string\n}\n\ninterface SharedProps {\n auth: {\n user: {\n id: number\n name: string\n }\n }\n flash: {\n message?: string\n category?: 'success' | 'error'\n }\n}\n\nexport default function Layout({ children, title }: LayoutProps) {\n const { auth, flash } = usePage().props\n\n return (\n
\n \n\n {flash.message && (\n
\n {flash.message}\n
\n )}\n\n
\n {title &&

{title}

}\n {children}\n
\n
\n )\n}\n```\n\nUse the layout in your pages:\n\n```tsx\nimport Layout from '@/components/Layout'\n\nexport default function Home({ message }: HomeProps) {\n return (\n \n

{message}

\n
\n )\n}\n```\n\n## Step 8: Add Global Styles\n\nCreate `frontend/globals.css`:\n\n```css\n/* Basic reset */\n*, *::before, *::after {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.5;\n}\n\n/* Or import Tailwind CSS */\n/* @tailwind base;\n@tailwind components;\n@tailwind utilities; */\n```\n\n## Running the Application\n\n### Development Mode\n\nStart both the Vite dev server and FastAPI:\n\n```bash\n# Terminal 1: Start Vite\nnpm run dev\n\n# Terminal 2: Start FastAPI\nuvicorn main:app --reload\n```\n\nOr create a `run-dev.sh` script:\n\n```bash\n#!/bin/bash\n# Start Vite in background\nnpm run dev &\nVITE_PID=$!\n\n# Start FastAPI\nuvicorn main:app --reload\n\n# Cleanup on exit\ntrap \"kill $VITE_PID\" EXIT\n```\n\n### Production Build\n\nBuild the frontend assets:\n\n```bash\nnpm run build\n```\n\nThis creates:\n- `static/build/.vite/manifest.json` - Asset manifest\n- `static/build/assets/` - Compiled JS and CSS files\n\nStart FastAPI (no Vite needed in production):\n\n```bash\nuvicorn main:app\n```\n\n## TypeScript Types\n\n### Page Props Types\n\nDefine types for your page components:\n\n```tsx\n// frontend/types.ts\nexport interface User {\n id: number\n name: string\n email: string\n}\n\nexport interface PaginatedResponse {\n data: T[]\n total: number\n page: number\n per_page: number\n has_more: boolean\n}\n```\n\n```tsx\n// frontend/pages/Users/Index.tsx\nimport { User, PaginatedResponse } from '@/types'\n\ninterface UsersIndexProps {\n users: PaginatedResponse\n}\n\nexport default function UsersIndex({ users }: UsersIndexProps) {\n return (\n
\n {users.data.map(user => (\n
{user.name}
\n ))}\n
\n )\n}\n```\n\n### Shared Props Types\n\nType your shared data:\n\n```tsx\n// frontend/types.ts\nexport interface SharedProps {\n auth: {\n user: User | null\n }\n flash: {\n message?: string\n category?: 'success' | 'error' | 'warning' | 'info'\n }\n // Add other shared props\n}\n```\n\n```tsx\nimport { usePage } from '@inertiajs/react'\nimport { SharedProps } from '@/types'\n\nexport default function Layout({ children }) {\n const { auth, flash } = usePage().props\n // ...\n}\n```\n\n## Common Patterns\n\n### Using Forms\n\n```tsx\nimport { useForm } from '@inertiajs/react'\n\nexport default function ContactForm() {\n const { data, setData, post, processing, errors } = useForm({\n name: '',\n email: '',\n message: '',\n })\n\n const submit = (e: React.FormEvent) => {\n e.preventDefault()\n post('/contact')\n }\n\n return (\n
\n setData('name', e.target.value)}\n />\n {errors.name && {errors.name}}\n\n \n \n )\n}\n```\n\n### Navigation with `router`\n\n```tsx\nimport { router } from '@inertiajs/react'\n\n// Navigate programmatically\nrouter.visit('/users/1')\n\n// With options\nrouter.visit('/users', {\n method: 'get',\n preserveState: true,\n preserveScroll: true,\n only: ['users'],\n})\n\n// POST request\nrouter.post('/users', { name: 'John' })\n\n// Reload current page\nrouter.reload()\n```\n\n### Progress Indicator\n\nAdd a loading indicator for navigation:\n\n```tsx\nimport { router } from '@inertiajs/react'\nimport { useEffect, useState } from 'react'\n\nexport default function Layout({ children }) {\n const [loading, setLoading] = useState(false)\n\n useEffect(() => {\n const start = () => setLoading(true)\n const finish = () => setLoading(false)\n\n router.on('start', start)\n router.on('finish', finish)\n\n return () => {\n router.off('start', start)\n router.off('finish', finish)\n }\n }, [])\n\n return (\n
\n {loading &&
}\n {children}\n
\n )\n}\n```\n\n## Next Steps\n\n- [Configuration](/guides/configuration/) - Customize Inertia settings\n- [Shared Data](/guides/shared-data/) - Pass data to all pages\n- [Validation Errors](/guides/validation-errors/) - Handle form errors\n- [Partial Reloads](/guides/partial-reloads/) - Optimize data loading", "toc": [{"id": "prerequisites", "text": "Prerequisites", "level": 2}, {"id": "project-structure", "text": "Project Structure", "level": 2}, {"id": "step-1-initialize-frontend", "text": "Step 1: Initialize Frontend", "level": 2}, {"id": "step-2-configure-vite", "text": "Step 2: Configure Vite", "level": 2}, {"id": "step-3-configure-typescript", "text": "Step 3: Configure TypeScript", "level": 2}, {"id": "step-4-create-the-inertia-app-entry-point", "text": "Step 4: Create the Inertia App Entry Point", "level": 2}, {"id": "dynamic-imports-alternative", "text": "Dynamic Imports (Alternative)", "level": 3}, {"id": "step-5-create-the-root-html-template", "text": "Step 5: Create the Root HTML Template", "level": 2}, {"id": "using-view-data-for-seo", "text": "Using View Data for SEO", "level": 3}, {"id": "step-6-create-your-first-page", "text": "Step 6: Create Your First Page", "level": 2}, {"id": "step-7-create-a-layout-component", "text": "Step 7: Create a Layout Component", "level": 2}, {"id": "step-8-add-global-styles", "text": "Step 8: Add Global Styles", "level": 2}, {"id": "running-the-application", "text": "Running the Application", "level": 2}, {"id": "development-mode", "text": "Development Mode", "level": 3}, {"id": "production-build", "text": "Production Build", "level": 3}, {"id": "typescript-types", "text": "TypeScript Types", "level": 2}, {"id": "page-props-types", "text": "Page Props Types", "level": 3}, {"id": "shared-props-types", "text": "Shared Props Types", "level": 3}, {"id": "common-patterns", "text": "Common Patterns", "level": 2}, {"id": "using-forms", "text": "Using Forms", "level": 3}, {"id": "navigation-with-router", "text": "Navigation with `router`", "level": 3}, {"id": "progress-indicator", "text": "Progress Indicator", "level": 3}, {"id": "next-steps", "text": "Next Steps", "level": 2}]}, "nav": [{"title": "Getting Started", "items": [{"title": "Introduction", "href": "/docs/"}, {"title": "Quick Start", "href": "/docs/quick-start/"}, {"title": "Django", "href": "/docs/django/"}, {"title": "Installation", "href": "/docs/installation/"}]}, {"title": "Core Concepts", "items": [{"title": "Pages", "href": "/docs/pages/"}, {"title": "Shared Data", "href": "/docs/shared-data/"}, {"title": "Links & Navigation", "href": "/docs/links/"}, {"title": "Forms", "href": "/docs/forms/"}]}, {"title": "Advanced", "items": [{"title": "Server-Side Rendering", "href": "/docs/ssr/"}, {"title": "SSR Lifespan", "href": "/docs/ssr-lifespan/"}, {"title": "Deferred Props", "href": "/docs/deferred-props/"}, {"title": "Experimental Lifespan Management", "href": "/docs/experimental-lifespan/"}, {"title": "History Encryption", "href": "/docs/history-encryption/"}, {"title": "Once Props", "href": "/docs/once-props/"}]}, {"title": "API Reference", "items": [{"title": "Inertia Class", "href": "/docs/api/inertia/"}, {"title": "Shared Data (FastAPI)", "href": "/docs/api/middleware/"}, {"title": "Configuration", "href": "/docs/api/configuration/"}]}, {"title": "Other", "items": [{"title": "Always Props", "href": "/docs/always-props/"}, {"title": "External Redirects", "href": "/docs/external-redirects/"}, {"title": "Frontend Setup", "href": "/docs/frontend-setup/"}, {"title": "Merge Props & Infinite Scroll", "href": "/docs/merge-props/"}, {"title": "Partial Reloads", "href": "/docs/partial-reloads/"}, {"title": "Prefetching", "href": "/docs/prefetching/"}, {"title": "Testing", "href": "/docs/testing/"}, {"title": "Validation Errors", "href": "/docs/validation-errors/"}, {"title": "View Data", "href": "/docs/view-data/"}]}], "currentPath": "/docs/frontend-setup/", "logoUrl": "/static/logo.svg", "logoInvertedUrl": "/static/logo-inverted.svg", "footerLogoUrl": "/static/logo-full.svg", "footerLogoInvertedUrl": "/static/logo-inverted.svg", "githubUrl": "https://github.com/patrick91/cross-inertia", "navLinks": [{"label": "Docs", "href": "/docs"}]}, "url": "/docs/frontend-setup/", "version": "f83124f236843b928916c1245f336129", "encryptHistory": false, "clearHistory": false}