Getting started: Laravel + Inertia
Laravel's React starter kit (Inertia 3): ThemeProvider goes in withApp, the theme attributes in the Blade template, and the starter's background and font give way to the theme
1. Create the app
Tested with Laravel 13.36 and Inertia 3.9. Without --stability=dev, Composer picks an outdated starter kit tag (Laravel 12, Inertia 2). The Composer scripts create .env, the app key and the SQLite database, and build the frontend for you.
composer create-project laravel/react-starter-kit my-app --stability=dev --no-interaction
cd my-app
2. Install the package
Works with react and react-dom 17, 18 and 19 (React 17: ids from useId appear right after mount). The package ships ES modules (plus a CommonJS copy for require()), has no runtime dependencies and has one file per module, so your bundle includes only the components you import; to load only their CSS as well, see “Only the CSS you need” below. Fonts are separate files: the vercel theme (the ThemeProvider default) uses Geist, so import valyro-ui/fonts/geist.css and valyro-ui/fonts/geist-mono.css next to the styles, or text falls back to the system font; glass uses system fonts and needs none. Until the package is published, build and pack it in the repository (npm run build:core && npm pack ./dist/core) and install the tarball from the repository root: npm install /path/to/ValyroUI/valyro-ui-0.1.0.tgz.
npm install valyro-ui
3. Styles and ThemeProvider
Inertia 3 has no setup() with <App /> — the root wrapper is set with the withApp callback. With SSR, pass locale to ThemeProvider. Otherwise numbers and dates are formatted in the server locale on the server and in the user locale in the browser, and hydration fails on the mismatched text (“1,200” vs “1 200”).
import { createInertiaApp } from '@inertiajs/react';
import 'valyro-ui/styles.css';
import { ThemeProvider } from 'valyro-ui';
import { Toaster } from '@/components/ui/sonner';
import { TooltipProvider } from '@/components/ui/tooltip';
import { initializeTheme } from '@/hooks/use-appearance';
import AppLayout from '@/layouts/app-layout';
import AuthLayout from '@/layouts/auth-layout';
import SettingsLayout from '@/layouts/settings/layout';
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
void createInertiaApp({
title: (title) => (title ? `${title} - ${appName}` : appName),
layout: (name) => {
switch (true) {
case name === 'welcome':
return null;
case name.startsWith('auth/'):
return AuthLayout;
case name.startsWith('settings/'):
return [AppLayout, SettingsLayout];
default:
return AppLayout;
}
},
strictMode: true,
withApp(app) {
return (
<ThemeProvider theme="glass" mode="light" locale="en-US">
<TooltipProvider delayDuration={0}>
{app}
<Toaster />
</TooltipProvider>
</ThemeProvider>
);
},
progress: {
color: '#4B5563',
},
});
// This will set light / dark mode on load...
initializeTheme();
4. Theme in the Blade template
Add the attributes to <html> and remove font-sans from <body>: through font: inherit in Tailwind's preflight, the starter's font overrides the theme font even in buttons. Remove @fonts from <head> too: it preloads the starter's Instrument Sans, which is then never used (three wasted downloads and “preloaded but not used” console warnings). The starter's own light/dark theme (the dark class) isn't synced with data-mode, so switch them together yourself.
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="glass" data-mode="light" @class(['dark' => ($appearance ?? 'system') == 'dark'])>
…
<body class="antialiased">
5. Remove the body background
A starter rule makes body opaque and hides the theme background, which is painted on <html>. Remove it from @layer base.
/* …the rest of the file stays as is… */
@layer base {
* {
@apply border-border;
}
}
6. Page
Showcase is the component from the Next.js guide, unchanged (in resources/js/components/showcase.tsx).
import { Head } from '@inertiajs/react';
import { Showcase } from '@/components/showcase';
export default function Welcome() {
return (
<>
<Head title="Welcome" />
<Showcase />
</>
);
}
7. Build and run
Without SSR, Inertia renders the page in the browser. With SSR (npm run build:ssr and php artisan inertia:start-ssr), the server-rendered HTML already contains the component markup, and hydration completes without warnings.
npm run build
php artisan serve