A PhpSPA boilerplate that serves PhpSPA pages first, then hydrates navigation with Vite + TypeScript + Tailwind.
|
π― Zero Config
|
β‘ Lightning Fast
|
|
π¨ Modern Stack
|
π¦ Everything Included
|
composer create-project phpspa/client my-project
cd my-project# Terminal 1 β Vite dev server with HMR
pnpm dev
# Terminal 2 β PHP built-in server
php -S localhost:8000 index.phpOpen http://localhost:8000 in your browser.
To enable Vite HMR during development, add these two lines to <script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/src/main.ts"></script> |
| Script | Purpose |
|---|---|
@vite/client |
π₯ Connects browser to Vite HMR server (port 5173) for instant hot module updates |
src/main.ts |
π¬ Your app entry point: imports styles, registers hooks, boots @dconco/phpspa runtime |
π‘ Pro tip: Leave these in during dev, remove them before deploying to production.
- In app/layout/layout.php we first try to fetch the Vite dev server (default
http://localhost:5173). - If the dev server is reachable, we use its HTML (HMR works).
- If itβs not reachable, we fall back to
index.htmland load the production assets frompublic/assets/.vite/manifest.json.
If your Vite server is not http://localhost:5173 (different host/port), you must update both:
index.html(the two<script type="module">URLs)- app/layout/layout.php (
$viteDevOrigin)
pnpm buildThis generates optimized, hashed assets in public/assets/ and creates the Vite manifest.
php -S localhost:8000 index.php
# Or deploy to Apache/NginxWhen dev scripts are missing, app/layout/layout.php automatically loads manifest assets.
Set APP_ENV=production in your environment.
- This disables the dev-server probe entirely (no extra network call in production).
- The layout always loads the manifest assets.
| Command | Description |
|---|---|
pnpm dev |
π§ Start Vite dev server with HMR on port 5173 |
pnpm build |
π¦ Production build to public/assets/ |
pnpm preview |
π Preview production build served by Vite |
pnpm watch |
ποΈ Continuous build mode for backend-only servers |
|
Add New Pages // app/pages/contact.php
return new Component(fn () => '<h1>Contact</h1>')
->route('/contact')
->title('Contact Us')Then attach in $app->attach(require 'app/pages/contact.php'); |
Add Client Hooks // src/main.ts
import { useEffect, setState } from '@dconco/phpspa';
useEffect(() => {
console.log('Page loaded!');
}, []); |
PhpSPA is PHP-first: every route returns real HTML on the first request (not an empty JS shell). That makes SEO straightforward:
- Search bots see content immediately (server-rendered HTML response).
- Dynamic SEO per route: set
title,description, OpenGraph, etc. right on the route component. - Global defaults: set shared meta tags once in
index.php.
Every route can define its own metadata:
// app/pages/HomePage.php
new Component($HomePage)
->route('/')
->title('PhpSPA Design System β Vite + Tailwind + PhpSPA')
->meta(name: 'description', content: 'Design-forward PhpSPA starter pairing PHP controllers with Vite, Tailwind, and typed state helpers for seamless SPA navigation.')
->meta(property: 'og:title', content: 'PhpSPA Design System β Vite + Tailwind + PhpSPA')
->meta(property: 'og:description', content: 'Explore PhpSPA component-driven PHP workflow, instant navigation, and production-ready Vite tooling.');You can do the same for every page (see
app/pages/AboutPage.phpandapp/pages/DocsPage.php).
Set shared defaults once in index.php:
new App()
->meta(charset: 'UTF-8')
->meta(name: 'viewport', content: 'width=device-width, initial-scale=1.0');In production you should output a canonical link per request. This template includes a production-only canonical example in index.php.
Issues and PRs welcome! Visit phpspa.tech for full documentation.
Built with β€οΈ using PhpSPA + Vite
Documentation β’ GitHub β’ NPM Package