Skip to content
Back to skills

Solid Impl Solidstart

ASecurity

Use when building SolidStart applications with SSR, file-based routing, or server functions. Prevents incorrect 'use server' placement, broken hydration, and misuse of query/createAsync patterns. Covers file-based routing, data loading with query and createAsync, server functions, actions with form integration, SSR streaming, hydration, and API routes. Keywords: SolidStart, use server, createAsync, query, file-based routing, SSR, hydration, API routes, Vinxi, Nitro.

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 10, 2026
developmenttypescriptshellreactnextjsnodeapi

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add OpenAEC-Foundation/OpenAEC-Workspace-Composer --skill solid-impl-solidstart --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Solid Impl Solidstart?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Solid Impl Solidstart
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/openaec-foundation-solid-impl-solidstart/badge)](https://www.skillsdirectory.com/skills/openaec-foundation-solid-impl-solidstart)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: solid-impl-solidstart
description: >
  Use when building SolidStart applications with SSR, file-based routing, or server functions.
  Prevents incorrect 'use server' placement, broken hydration, and misuse of query/createAsync patterns.
  Covers file-based routing, data loading with query and createAsync, server functions, actions with form integration, SSR streaming, hydration, and API routes.
  Keywords: SolidStart, use server, createAsync, query, file-based routing, SSR, hydration, API routes, Vinxi, Nitro.
license: MIT
compatibility: "Designed for Claude Code. Requires SolidStart 1.x with SolidJS 1.x/2.x and TypeScript."
metadata:
  author: OpenAEC-Foundation
  version: "1.0"
---

# solid-impl-solidstart

## Quick Reference

### Architecture Stack

| Layer | Technology | Role |
|-------|-----------|------|
| SolidJS | Fine-grained reactivity | Component rendering, signals, stores |
| Vite | Bundler | Dev server, HMR, production builds |
| Nitro | Server runtime | Deployment adapters, server functions |
| Vinxi | CLI + config | Combines Vite + Nitro into unified tooling |

### Project Structure

```
src/
├── routes/              # File-based routing (ALWAYS here)
│   ├── index.tsx        # / route
│   ├── about.tsx        # /about route
│   └── api/             # API routes
├── entry-client.tsx     # Client hydration entry
├── entry-server.tsx     # Server request handler
└── app.tsx              # Root component (Router + FileRoutes)
```

### Minimum app.tsx

```tsx
import { Suspense } from "solid-js";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";

export default function App() {
  return (
    <Router root={(props) => <Suspense>{props.children}</Suspense>}>
      <FileRoutes />
    </Router>
  );
}
```

### Critical Warnings

**ALWAYS** make `"use server"` functions `async` -- the directive requires the function to return a `Promise`. Non-async server functions cause runtime errors.

**ALWAYS** `throw redirect()` from server functions -- NEVER `return redirect()`. Redirect is implemented as a thrown response, not a return value. Returning it does nothing.

**NEVER** use `getServerSideProps`, `useLoaderData`, or any Next.js/React Router data loading patterns. SolidStart uses `query()` + `createAsync()` exclusively.

**NEVER** use `useEffect` for data fetching -- SolidStart has NO equivalent of React's `useEffect` data fetching pattern. ALWAYS use `query()` + `createAsync()`.

**NEVER** use `element={<Component />}` on Route -- ALWAYS use `component={Component}`. The React pattern creates the component immediately, breaking Solid's deferred rendering.

**NEVER** use the deprecated 0.x APIs (`createServerData$`, `createServerAction$`) -- ALWAYS use `query()`, `action()`, and `"use server"` from SolidStart 1.0+.

---

## File-Based Routing Convention Table

| File Pattern | URL | Description |
|-------------|-----|-------------|
| `routes/index.tsx` | `/` | Home route |
| `routes/about.tsx` | `/about` | Static route |
| `routes/blog/article.tsx` | `/blog/article` | Nested route |
| `routes/blog.tsx` + `routes/blog/` | Layout wrapper | Layout for `/blog/*` children |
| `routes/users/[id].tsx` | `/users/:id` | Dynamic parameter |
| `routes/users/[[id]].tsx` | `/users` or `/users/:id` | Optional parameter |
| `routes/docs/[...path].tsx` | `/docs/*` | Catch-all parameter |
| `routes/(admin)/dashboard.tsx` | `/dashboard` | Route group (no URL segment) |
| `routes/api/users.ts` | `/api/users` | API route (no default export) |

### Dynamic Route Parameters

```tsx
import { useParams } from "@solidjs/router";

// routes/users/[id].tsx
export default function UserPage() {
  const params = useParams();
  return <div>User: {params.id}</div>;
}

// routes/docs/[...path].tsx -- catch-all
export default function DocsPage() {
  const params = useParams();
  // params.path = "guides/getting-started" (slash-delimited)
  return <div>Doc: {params.path}</div>;
}
```

### Nested Layout Pattern

```tsx
// routes/blog.tsx -- layout wrapper for /blog/*
import type { RouteSectionProps } from "@solidjs/router";

export default function BlogLayout(props: RouteSectionProps) {
  return (
    <div>
      <nav>Blog Navigation</nav>
      {props.children}
    </div>
  );
}
```

---

## Decision Tree: Data Loading

```
Need server-side data?
├─ YES: Is it a read operation (query)?
│  ├─ YES: Use query() + createAsync()
│  │  └─ Need fine-grained store updates? → Use createAsyncStore()
│  └─ NO: It's a mutation → Use action() (see Actions section)
└─ NO: Client-only data
   └─ Use createSignal/createResource directly
```

---

## Data Loading Pattern

### Step 1: Define a Query with Server Function

```tsx
import { query } from "@solidjs/router";

const getUser = query(async (id: string) => {
  "use server";
  const user = await db.getUser(id);
  if (!user) throw new Error("User not found");
  return user;
}, "user");
```

### Step 2: Consume with createAsync

```tsx
import { createAsync } from "@solidjs/router";
import { Suspense } from "solid-js";

export default function UserPage() {
  const params = useParams();
  const user = createAsync(() => getUser(params.id));

  return (
    <Suspense fallback={<p>Loading...</p>}>
      <h1>{user()?.name}</h1>
    </Suspense>
  );
}
```

### Step 3: Preload for Instant Navigation

```tsx
import type { RouteDefinition } from "@solidjs/router";

export const route = {
  preload({ params }) {
    getUser(params.id); // fire-and-forget, warms cache
  },
} satisfies RouteDefinition;
```

---

## Decision Tree: Mutations

```
Need to mutate server data?
├─ From a <form>?
│  ├─ YES: Use action={myAction} method="post"
│  │  └─ Need extra args? → Use action={myAction.with(extraArg)}
│  └─ NO: Programmatic mutation
│     └─ Use useAction(myAction) for imperative calls
└─ Track submission state?
   ├─ Single submission → useSubmission(myAction)
   └─ Multiple concurrent → useSubmissions(myAction)
```

---

## Actions and Forms Pattern

### Form-Based Action

```tsx
import { action, redirect } from "@solidjs/router";

const addTodo = action(async (formData: FormData) => {
  "use server";
  const title = formData.get("title") as string;
  await db.addTodo(title);
  throw redirect("/todos");  // ALWAYS throw, NEVER return
}, "addTodo");

export default function NewTodo() {
  return (
    <form action={addTodo} method="post">
      <input name="title" required />
      <button type="submit">Add</button>
    </form>
  );
}
```

### Programmatic Action with useAction

```tsx
import { action, useAction } from "@solidjs/router";

const toggleTodo = action(async (id: string) => {
  "use server";
  await db.toggleTodo(id);
}, "toggleTodo");

function TodoItem(props: { id: string; done: boolean }) {
  const doToggle = useAction(toggleTodo);
  return (
    <button onClick={() => doToggle(props.id)}>
      {props.done ? "Undo" : "Done"}
    </button>
  );
}
```

### Submission Tracking

```tsx
import { useSubmission } from "@solidjs/router";

function TodoForm() {
  const submission = useSubmission(addTodo);
  return (
    <form action={addTodo} method="post">
      <input name="title" />
      <button disabled={submission.pending}>
        {submission.pending ? "Adding..." : "Add"}
      </button>
    </form>
  );
}
```

### Response Helpers After Actions

```tsx
import { redirect, reload, revalidate } from "@solidjs/router";

const updateUser = action(async (id: string, formData: FormData) => {
  "use server";
  await db.updateUser(id, Object.fromEntries(formData));
  // Option 1: Redirect (ALWAYS throw)
  throw redirect("/users");
  // Option 2: Revalidate specific query + redirect
  throw redirect("/users", { revalidate: getUser.keyFor(id) });
  // Option 3: Reload current page
  throw reload({ revalidate: getUser.key });
}, "updateUser");
```

---

## Server Functions

### Function-Level "use server"

```tsx
const getData = async (id: string) => {
  "use server";
  return db.getData(id);  // Runs server-only, compiled to RPC
};
```

### File-Level "use server"

```tsx
// src/lib/server/queries.ts
"use server";

export async function getUsers() {
  return db.getUsers();
}

export async function getProduct(id: string) {
  return db.getProduct(id);
}
```

### Serialization Requirement

Arguments and return values MUST be serializable (SolidStart uses Seroval). NEVER pass DOM nodes, functions, or class instances through server functions.

---

## SSR and Hydration

| Mode | Function | Use Case |
|------|----------|----------|
| Sync SSR | `renderToString` | Static pages, no async data |
| Streaming SSR | `renderToStream` | Dynamic pages with Suspense boundaries |
| Client hydration | `hydrate` | Attach reactivity to server-rendered HTML |

### Streaming SSR (Default in SolidStart)

```tsx
import { renderToStream } from "solid-js/web";

const stream = renderToStream(() => <App />, {
  onCompleteShell() {
    // Shell rendered, Suspense fallbacks in place
  },
  onCompleteAll() {
    // All async content resolved
  },
});
stream.pipe(res);       // Node.js
stream.pipeTo(writable); // Web Streams
```

### HydrationScript

ALWAYS include `<HydrationScript />` once in the HTML head. It captures user events (click, input) before Solid loads, then replays them after hydration.

### isServer

```tsx
import { isServer } from "solid-js/web";

if (isServer) {
  // Tree-shaken from client bundle at build time
}
```

---

## API Routes

API routes export named HTTP method handlers (NOT a default component):

```tsx
// routes/api/users/[id].ts
import type { APIEvent } from "@solidjs/start/server";

export async function GET({ params }: APIEvent) {
  const user = await db.getUser(params.id);
  if (!user) return new Response("Not found", { status: 404 });
  return user; // Auto-serialized to JSON
}

export async function DELETE({ params }: APIEvent) {
  await db.deleteUser(params.id);
  return new Response(null, { status: 204 });
}
```

API routes are prioritized over UI routes at the same path.

---

## SolidStart 0.x to 1.0 Migration

| 0.x (Deprecated) | 1.0 (Current) |
|-------------------|----------------|
| `createServerData$` | `query()` + `createAsync()` |
| `createServerAction$` | `action()` |
| `createRouteData` | `query()` + `createAsync()` |
| `useRouteData()` | `createAsync(() => myQuery())` |
| `server$()` wrapper | `"use server"` directive |

---

## Reference Links

- [references/methods.md](references/methods.md) -- API signatures for query, createAsync, action, redirect, renderToStream, hydrate
- [references/examples.md](references/examples.md) -- File routing, data loading, server functions, action/form, SSR, API route patterns
- [references/anti-patterns.md](references/anti-patterns.md) -- Next.js patterns, getServerSideProps, useEffect data fetching, React Router patterns

### Official Sources

- https://docs.solidjs.com/solid-start
- https://docs.solidjs.com/solid-start/building-your-application/routing
- https://docs.solidjs.com/solid-start/reference/server/use-server
- https://docs.solidjs.com/solid-router/reference/data-apis/query
- https://docs.solidjs.com/solid-router/reference/data-apis/action
- https://docs.solidjs.com/solid-router/reference/data-apis/create-async
- https://docs.solidjs.com/reference/rendering/render-to-stream
- https://docs.solidjs.com/reference/rendering/hydrate
- https://docs.solidjs.com/solid-start/building-your-application/api-routes

Files in this skill

  • SKILL.md11.3 KB
  • references/anti-patterns.md8.8 KB
  • references/examples.md10.9 KB
  • references/methods.md7.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.