From init-stack
Scaffold a new full-stack project with TanStack Start + Cloudflare Workers + shadcn/ui + Tailwind CSS v4 + Hugeicons + bun. Use this skill whenever the user says "init project", "bootstrap a new app", "set up the stack", "create a new project", "start a new app", or any variation of initializing a fresh codebase with this tech stack. Also trigger when the user asks to recreate the boilerplate, start from scratch, or set up a new repo with TanStack, Cloudflare Workers, or shadcn. Do not wait for the user to list every technology — if it sounds like they want a new project and this stack is in scope, use this skill.
npx claudepluginhub fuongz/skills --plugin init-stackThis skill uses the workspace's default tool permissions.
Bootstrap a production-ready full-stack app: **TanStack Start** · **Cloudflare Workers** · **shadcn/ui** · **Tailwind CSS v4** · **Hugeicons** · **bun**
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
Bootstrap a production-ready full-stack app: TanStack Start · Cloudflare Workers · shadcn/ui · Tailwind CSS v4 · Hugeicons · bun
Project name/path (if provided): $ARGUMENTS
If $ARGUMENTS is empty, ask the user: "What's the project name and where should I create it?" Otherwise, use the provided name/path directly. The project name goes into wrangler.jsonc and package.json.
cd into the target directory before running any commands.
bun init -y
rm -f index.ts README.md # clean up bun's default files
# Runtime deps
bun add @tanstack/react-start @tanstack/react-router react react-dom tailwindcss @tailwindcss/vite
# Dev deps
bun add -D vite @vitejs/plugin-react @types/react @types/react-dom @cloudflare/vite-plugin wrangler typescript @biomejs/biome
bunx biome init
Then overwrite biome.json with the following (uses local $schema from node_modules):
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": ["**", "!**/src/routeTree.gen.ts"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
},
"css": {
"parser": {
"tailwindDirectives": true
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}
tsconfig.jsonWhy these settings matter:
moduleResolution: "Bundler"is required for Vite's import resolution to work correctly.verbatimModuleSyntaxmust be omitted — TanStack Start docs explicitly warn it causes server bundle leakage into client bundles.- The
@/*alias maps tosrc/*for clean imports throughout the project.
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"strictNullChecks": true,
"strict": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src", "vite.config.ts"]
}
vite.config.tsPlugin order is critical.
cloudflare()must come beforetanstackStart(), andviteReact()must come aftertanstackStart(). Thecloudflareplugin targets the SSR environment so the Workers bundle is created correctly.
import { resolve } from "node:path";
import { cloudflare } from "@cloudflare/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
tailwindcss(),
cloudflare({ viteEnvironment: { name: "ssr" } }),
tanstackStart(),
// React plugin must come AFTER TanStack Start plugin
viteReact(),
],
resolve: {
alias: {
"@": resolve(__dirname, "./src"),
},
},
});
Why
resolve.aliasinstead ofvite-tsconfig-paths: The@cloudflare/vite-pluginsets up a custom SSR environment that can conflict withvite-tsconfig-paths. The explicit alias is simpler, has no extra dependency, and reliably resolves@/*imports in both client and Worker bundles.
wrangler.jsoncReplace <project-name> with the actual project name.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "<project-name>",
"compatibility_date": "2025-09-02",
"compatibility_flags": ["nodejs_compat"],
"main": "@tanstack/react-start/server-entry",
"assets": {
"directory": ".output/static",
"binding": "ASSETS"
}
}
package.jsonReplace the bun init defaults with proper scripts. Keep all dependencies that were installed.
{
"name": "<project-name>",
"type": "module",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"deploy": "bun run build && wrangler deploy",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"cf-typegen": "wrangler types"
}
}
Create the following files exactly as shown.
src/styles/globals.css@import "tailwindcss";
(shadcn init will expand this file with the full theme in Step 9)
src/router.tsximport { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
// Required by TanStack Start v1.166+ — the server calls getRouter() at runtime
export async function getRouter() {
return createRouter()
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
src/routes/__root.tsx/// <reference types="vite/client" />
import type { ReactNode } from 'react'
import '../styles/globals.css'
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/react-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ title: '<project-name>' },
],
links: [{ rel: 'icon', href: '/favicon.ico' }],
}),
component: RootComponent,
notFoundComponent: NotFoundComponent,
})
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
)
}
function NotFoundComponent() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold">404</h1>
<p className="mt-2 text-muted-foreground">Page not found</p>
</div>
</div>
)
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
src/routes/index.tsximport { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold tracking-tight"><project-name></h1>
<p className="mt-2 text-muted-foreground">Ready to build.</p>
</div>
</div>
)
}
routeTree.gen.tsRun the dev server briefly — it will auto-generate src/routeTree.gen.ts on first start, then stop it.
# Start dev server, wait ~5 seconds, then Ctrl+C
bun run dev
Confirm src/routeTree.gen.ts now exists before proceeding.
bunx --bun shadcn@latest init --preset aLrO8A --base base --template start
This preset configures:
base-nova style with OKLCH colour system (light + dark)src/styles/globals.css@/* import aliases in components.jsonbun add @hugeicons/react @hugeicons/core-free-icons
Usage pattern going forward:
import { HugeiconsIcon } from '@hugeicons/react'
import { HomeIcon } from '@hugeicons/core-free-icons'
<HugeiconsIcon icon={HomeIcon} />
bun run typecheck
Must exit with zero errors. If there are errors, fix them before declaring the project ready.
Tell the user:
bun run dev → http://localhost:5173bunx --bun shadcn@latest add <component>wrangler.jsonc, then regenerate types with bun run cf-typegencreateServerFn from @tanstack/react-startbun run deployfs, path, process.env, etc. This is Cloudflare Workers only. Exception: node:path is fine in vite.config.ts (build-time only).globals.css under @theme inline.routeTree.gen.ts — it is auto-generated by the Vite plugin.vite.config.ts will break the build.verbatimModuleSyntax — never add this to tsconfig.json.getRouter is required — TanStack Start v1.166+ calls getRouter() at runtime on the router entry. Without it, every request crashes with entries.routerEntry.getRouter is not a function.@ alias must be in vite.config.ts — tsconfig.json paths alone aren't enough; Vite needs the explicit resolve.alias. Don't use vite-tsconfig-paths — it can conflict with the Cloudflare SSR environment.