Welcome to FUNSTACK Static! Build high-performance Single Page Applications powered by React Server Components - no server required at runtime.
Install @funstack/static and its peer dependencies:
npm install @funstack/static react react-dom
Or with pnpm:
pnpm add @funstack/static react react-dom
Create or update your vite.config.ts:
import funstackStatic from "@funstack/static";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
funstackStatic({
root: "./src/Root.tsx",
app: "./src/App.tsx",
}),
react(),
],
});
The Root component wraps your entire application and defines the HTML structure.
// src/Root.tsx
import type React from "react";
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
</head>
<body>{children}</body>
</html>
);
}
The Root component:
The App component defines what comes into the children of the Root. This is the entrypoint of your SPA.
Since the App component is a server component, for interactivity you can import and use client components inside it.
// src/App.tsx
import Counter from "./Counter";
export default function App() {
return (
<main>
<h1>Welcome to my site!</h1>
<Counter />
</main>
);
}
// src/Counter.tsx
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
The App component:
If you want routing, you can bring your favorite SPA router. Here, we use @funstack/router as an example:
// src/App.tsx
import { Router } from "@funstack/router";
import { route } from "@funstack/router/server";
const routes = [
route({
path: "/",
component: <h1>Welcome to my site!</h1>,
}),
route({
path: "/about",
component: <h1>About Us</h1>,
}),
];
export default function App() {
return <Router routes={routes} />;
}
npm run dev
Your site is now running at http://localhost:5173!
npm run build
Your SPA is pre-rendered to static files in the dist/public directory, ready to deploy to any static hosting provider.
Since FUNSTACK Static is built on top of Vite, any Vite configurations and plugins should work seamlessly.
A typical FUNSTACK Static project looks like this:
my-site/
├── src/
│ ├── ...
│ ├── Root.tsx # HTML wrapper component
│ └── App.tsx # Routes and main app
├── public/ # Static assets
├── vite.config.ts # Vite configuration
└── package.json
Only two files are required: Root.tsx and App.tsx. The paths to these files can be customized in the funstackStatic() configuration.
If you use AI coding assistants like Claude Code, you can install the FUNSTACK Static knowledge skill to help your AI assistant better understand the framework:
npx -p @funstack/static funstack-static-skill-installer
# or
yarn dlx -p @funstack/static funstack-static-skill-installer
# or
pnpm --package @funstack/static dlx funstack-static-skill-installer
# or, if you use skills CLI (https://skills.sh/)
npx skills add uhyo/funstack-static
This registers the funstack-static-knowledge skill, which provides your AI assistant with API references, best practices, and architectural guidance for FUNSTACK Static.