.claude/skills/solidjs/solid-agents/solid-agents-project-scaffolder/SKILL.md
Use when creating a new SolidJS project, scaffolding a SolidStart application, or setting up project infrastructure. Prevents misconfigured Vite/TypeScript settings, wrong directory structures, and React-style project patterns. Covers Vite configuration, TypeScript setup, component directory structure, routing, state management, and testing setup. Keywords: SolidJS scaffold, SolidStart project, Vite config, tsconfig, project template, file structure, routing setup.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer solid-agents-project-scaffolderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Need a new SolidJS project?
├── Need SSR, file-based routing, server functions, or API routes?
│ └── YES → SolidStart scaffold
│ ├── Full-stack app with data loading? → SolidStart with query/action
│ ├── Static site with prerendering? → SolidStart with SSG preset
│ └── API backend + frontend? → SolidStart with API routes
└── NO → Plain SolidJS scaffold (client-side SPA)
├── Simple reactive UI? → Plain SolidJS + Vite
├── Need routing? → Plain SolidJS + @solidjs/router
└── Embedded widget or library? → Plain SolidJS minimal
NEVER use create-react-app, next, or any React scaffolding tool for a SolidJS project. The generated configuration, babel presets, and JSX transform are incompatible.
NEVER omit vite-plugin-solid from vite.config.ts. Without it, JSX compilation fails silently or produces React output.
ALWAYS set "jsxImportSource": "solid-js" in tsconfig.json. Without this, TypeScript resolves JSX types from React, causing type errors on SolidJS-specific attributes.
ALWAYS use babel-preset-solid (included via vite-plugin-solid). This preset compiles JSX into SolidJS's fine-grained reactive DOM operations instead of React.createElement calls.
NEVER include react, react-dom, @types/react, or @types/react-dom in dependencies. Their presence causes type conflicts and import confusion.
ALWAYS use solid-js/web for render and hydrate — these are NOT imported from solid-js directly.
| Package | Type | Purpose |
|---------|------|---------|
| solid-js | dependency | Core reactive framework |
| @solidjs/router | dependency | Client-side routing (if needed) |
| typescript | devDependency | TypeScript compiler |
| vite | devDependency | Build tool and dev server |
| vite-plugin-solid | devDependency | Vite integration for SolidJS JSX compilation |
| vitest | devDependency | Testing framework |
| @solidjs/testing-library | devDependency | Component testing utilities |
| jsdom | devDependency | DOM environment for tests |
my-solid-app/
├── public/
│ └── favicon.ico
├── src/
│ ├── components/
│ │ └── Counter.tsx
│ ├── context/
│ │ └── AppContext.tsx
│ ├── lib/
│ │ └── utils.ts
│ ├── App.tsx
│ └── index.tsx
├── test/
│ └── Counter.test.tsx
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
├── vitest.config.ts
└── .gitignore
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";
export default defineConfig({
plugins: [solidPlugin()],
server: {
port: 3000,
},
build: {
target: "esnext",
},
});
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My Solid App</title>
</head>
<body>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>
import { render } from "solid-js/web";
import App from "./App";
const root = document.getElementById("root");
if (!root) throw new Error("Root element not found");
render(() => <App />, root);
| Package | Type | Purpose |
|---------|------|---------|
| solid-js | dependency | Core reactive framework |
| @solidjs/router | dependency | Routing (file-based + programmatic) |
| @solidjs/start | dependency | SolidStart meta-framework |
| @solidjs/meta | dependency | Document head management |
| vinxi | devDependency | Build orchestration (Vite + Nitro) |
| typescript | devDependency | TypeScript compiler |
| vitest | devDependency | Testing framework |
| @solidjs/testing-library | devDependency | Component testing utilities |
| jsdom | devDependency | DOM environment for tests |
my-solidstart-app/
├── public/
│ └── favicon.ico
├── src/
│ ├── components/
│ │ └── Counter.tsx
│ ├── context/
│ │ └── AppContext.tsx
│ ├── lib/
│ │ └── utils.ts
│ ├── routes/
│ │ ├── api/
│ │ │ └── hello.ts
│ │ ├── about.tsx
│ │ └── index.tsx
│ ├── app.tsx
│ ├── entry-client.tsx
│ └── entry-server.tsx
├── test/
│ └── Counter.test.tsx
├── app.config.ts
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── .gitignore
import { defineConfig } from "@solidjs/start/config";
export default defineConfig({});
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vinxi/types/client"],
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"paths": {
"~/*": ["./src/*"]
}
},
"include": ["src"]
}
import { Suspense } from "solid-js";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { MetaProvider } from "@solidjs/meta";
export default function App() {
return (
<Router
root={(props) => (
<MetaProvider>
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
}
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client";
mount(() => <StartClient />, document.getElementById("app")!);
import { createHandler, StartServer } from "@solidjs/start/server";
export default createHandler(() => (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
));
ALWAYS use this pattern for shared application state. NEVER use React's useReducer or Redux patterns.
// src/context/AppContext.tsx
import { createContext, useContext, type ParentProps } from "solid-js";
import { createStore } from "solid-js/store";
interface AppState {
user: { name: string; email: string } | null;
theme: "light" | "dark";
}
interface AppActions {
setUser: (user: AppState["user"]) => void;
toggleTheme: () => void;
}
const AppContext = createContext<[AppState, AppActions]>();
export function AppProvider(props: ParentProps) {
const [state, setState] = createStore<AppState>({
user: null,
theme: "light",
});
const actions: AppActions = {
setUser: (user) => setState("user", user),
toggleTheme: () =>
setState("theme", (prev) => (prev === "light" ? "dark" : "light")),
};
return (
<AppContext.Provider value={[state, actions]}>
{props.children}
</AppContext.Provider>
);
}
export function useApp(): [AppState, AppActions] {
const context = useContext(AppContext);
if (!context) throw new Error("useApp must be used within AppProvider");
return context;
}
import { defineConfig } from "vitest/config";
import solidPlugin from "vite-plugin-solid";
export default defineConfig({
plugins: [solidPlugin()],
test: {
environment: "jsdom",
globals: true,
transformMode: {
web: [/\.[jt]sx?$/],
},
},
resolve: {
conditions: ["development", "browser"],
},
});
import { render, fireEvent, screen } from "@solidjs/testing-library";
import { describe, it, expect } from "vitest";
import Counter from "../src/components/Counter";
describe("Counter", () => {
it("increments count on click", async () => {
render(() => <Counter />);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("Count: 0");
fireEvent.click(button);
expect(button).toHaveTextContent("Count: 1");
});
});
When adding routing to a plain SolidJS project, ALWAYS use @solidjs/router with component prop (NEVER use element prop like React Router).
// src/App.tsx (with routing)
import { Router, Route } from "@solidjs/router";
import { lazy } from "solid-js";
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const NotFound = lazy(() => import("./pages/NotFound"));
export default function App() {
return (
<Router>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="*404" component={NotFound} />
</Router>
);
}
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.