Fix: SolidStart Not Working — Routes Not Rendering, Server Functions Failing, or Hydration Errors
Quick Answer
How to fix SolidStart issues — file-based routing, server functions, createAsync data loading, middleware, sessions, and deployment configuration.
The Problem
A SolidStart route renders blank:
GET /about → blank page, no contentOr a server function throws:
'use server';
async function getUsers() {
return db.query.users.findMany();
}
// Error: db is not defined — or — 'use server' is not recognizedOr the page hydrates incorrectly:
Hydration mismatch: server rendered "Hello" but client rendered ""Why This Happens
SolidStart is SolidJS’s meta-framework, similar to Next.js but for Solid. It uses Vinxi under the hood:
- SolidStart uses file-based routing in
src/routes/— files and directories map to URL paths. A missing or misplaced file means no route. - Server functions need
'use server'directive — code marked with'use server'runs only on the server. Without the directive, server-only code (database, fs) is bundled into the client and crashes. - SolidJS reactivity is different from React — Solid uses signals and fine-grained reactivity. Destructuring props or accessing signals outside of JSX/effects breaks reactivity.
- Hydration requires matching server/client output — if the server renders different HTML than the client expects (e.g., browser-only code runs during SSR), hydration fails.
Fix 1: Project Setup
npm init solid@latest my-app
# Select: SolidStart, TypeScript
cd my-app && npm install && npm run dev// app.config.ts — SolidStart configuration
import { defineConfig } from '@solidjs/start/config';
export default defineConfig({
server: {
preset: 'node-server', // 'vercel' | 'netlify' | 'cloudflare-pages'
},
vite: {
// Vite plugins and config
},
});src/routes/
├── index.tsx # /
├── about.tsx # /about
├── [...404].tsx # Catch-all 404
├── posts/
│ ├── index.tsx # /posts
│ └── [id].tsx # /posts/:id
├── (auth)/
│ ├── login.tsx # /login (group doesn't add URL segment)
│ └── register.tsx # /register
└── api/
├── users.ts # /api/users (API route)
└── posts/
└── [id].ts # /api/posts/:idFix 2: Routes and Data Loading
// src/routes/index.tsx — home page
import { A } from '@solidjs/router';
export default function Home() {
return (
<main>
<h1>Welcome to SolidStart</h1>
<A href="/posts">View Posts</A>
</main>
);
}// src/routes/posts/index.tsx — list with server data
import { createAsync, query } from '@solidjs/router';
import { For, Suspense } from 'solid-js';
// Define a query — cached and deduplicated
const getPosts = query(async () => {
'use server';
return db.query.posts.findMany({
where: eq(posts.published, true),
orderBy: desc(posts.createdAt),
});
}, 'posts');
// Route data — preloaded before component renders
export const route = {
preload: () => getPosts(),
};
export default function PostsPage() {
const posts = createAsync(() => getPosts());
return (
<main>
<h1>Blog Posts</h1>
<Suspense fallback={<p>Loading posts...</p>}>
<For each={posts()}>
{(post) => (
<article>
<A href={`/posts/${post.id}`}>
<h2>{post.title}</h2>
</A>
<p>{post.excerpt}</p>
</article>
)}
</For>
</Suspense>
</main>
);
}// src/routes/posts/[id].tsx — dynamic route
import { createAsync, query, useParams } from '@solidjs/router';
import { Show, Suspense } from 'solid-js';
const getPost = query(async (id: string) => {
'use server';
const post = await db.query.posts.findFirst({
where: eq(posts.id, id),
});
if (!post) throw new Error('Post not found');
return post;
}, 'post');
export const route = {
preload: ({ params }: { params: { id: string } }) => getPost(params.id),
};
export default function PostPage() {
const params = useParams();
const post = createAsync(() => getPost(params.id));
return (
<Suspense fallback={<p>Loading...</p>}>
<Show when={post()} fallback={<p>Post not found</p>}>
{(p) => (
<article>
<h1>{p().title}</h1>
<div innerHTML={p().htmlContent} />
</article>
)}
</Show>
</Suspense>
);
}Fix 3: Server Functions and Actions
// src/lib/server.ts — server functions
'use server';
import { db } from './db';
export async function getUsers() {
return db.query.users.findMany();
}
export async function createUser(name: string, email: string) {
const [user] = await db.insert(users).values({ name, email }).returning();
return user;
}
export async function deleteUser(id: string) {
await db.delete(users).where(eq(users.id, id));
}// src/routes/users.tsx — using server functions with actions
import { createAsync, query, action, useAction, useSubmission } from '@solidjs/router';
import { createUser, deleteUser, getUsers } from '~/lib/server';
const getUsersQuery = query(async () => {
'use server';
return getUsers();
}, 'users');
// Define actions for mutations
const createUserAction = action(async (formData: FormData) => {
'use server';
const name = formData.get('name') as string;
const email = formData.get('email') as string;
await createUser(name, email);
});
const deleteUserAction = action(async (id: string) => {
'use server';
await deleteUser(id);
});
export default function UsersPage() {
const users = createAsync(() => getUsersQuery());
const submission = useSubmission(createUserAction);
return (
<div>
<h1>Users</h1>
{/* Form with action — progressive enhancement */}
<form action={createUserAction} method="post">
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit" disabled={submission.pending}>
{submission.pending ? 'Adding...' : 'Add User'}
</button>
</form>
<Suspense fallback={<p>Loading...</p>}>
<For each={users()}>
{(user) => (
<div>
<span>{user.name} ({user.email})</span>
<button onClick={() => {
const del = useAction(deleteUserAction);
del(user.id);
}}>
Delete
</button>
</div>
)}
</For>
</Suspense>
</div>
);
}Fix 4: Layouts
// src/routes/(app).tsx — layout for authenticated routes
import { A, Outlet } from '@solidjs/router';
export default function AppLayout() {
return (
<div class="flex min-h-screen">
<nav class="w-64 bg-gray-900 text-white p-4">
<h2 class="text-xl font-bold mb-4">My App</h2>
<ul class="space-y-2">
<li><A href="/" class="hover:text-blue-400">Home</A></li>
<li><A href="/posts" class="hover:text-blue-400">Posts</A></li>
<li><A href="/settings" class="hover:text-blue-400">Settings</A></li>
</ul>
</nav>
<main class="flex-1 p-8">
<Outlet />
</main>
</div>
);
}
// src/app.tsx — root component
import { Router } from '@solidjs/router';
import { FileRoutes } from '@solidjs/start/router';
import { Suspense } from 'solid-js';
import './app.css';
export default function App() {
return (
<Router root={(props) => (
<Suspense>{props.children}</Suspense>
)}>
<FileRoutes />
</Router>
);
}Fix 5: API Routes
// src/routes/api/users.ts — REST API endpoint
import { json } from '@solidjs/router';
import type { APIEvent } from '@solidjs/start/server';
export async function GET(event: APIEvent) {
const users = await db.query.users.findMany();
return json(users);
}
export async function POST(event: APIEvent) {
const body = await event.request.json();
const [user] = await db.insert(users).values(body).returning();
return json(user, { status: 201 });
}
// src/routes/api/users/[id].ts
export async function GET(event: APIEvent) {
const id = event.params.id;
const user = await db.query.users.findFirst({ where: eq(users.id, id) });
if (!user) return json({ error: 'Not found' }, { status: 404 });
return json(user);
}
export async function DELETE(event: APIEvent) {
const id = event.params.id;
await db.delete(users).where(eq(users.id, id));
return new Response(null, { status: 204 });
}Fix 6: Middleware and Sessions
// src/middleware.ts
import { createMiddleware } from '@solidjs/start/middleware';
export default createMiddleware({
onRequest: [
// Auth middleware
async (event) => {
const token = event.request.headers.get('authorization')?.replace('Bearer ', '');
if (token) {
try {
const user = await verifyToken(token);
event.locals.user = user;
} catch {
// Invalid token — continue without user
}
}
},
// Logging middleware
async (event) => {
console.log(`${event.request.method} ${event.request.url}`);
},
],
});
// Access in server functions
'use server';
import { getRequestEvent } from 'solid-js/web';
export async function getCurrentUser() {
const event = getRequestEvent();
return event?.locals.user ?? null;
}Still Not Working?
Blank page — check that the route file is in src/routes/ and exports a default component. Also verify src/app.tsx includes <FileRoutes /> inside a <Router>.
“use server” not recognized — ensure the string 'use server' is at the very top of the function or file. It must be a string literal, not a variable. Also check that the SolidStart Vite plugin is configured in app.config.ts.
Reactivity doesn’t work — SolidJS signals must be called as functions in JSX: {count()} not {count}. Don’t destructure props: use props.name instead of const { name } = props. Don’t access signals outside of JSX or createEffect.
Hydration mismatch — code that uses window, document, or localStorage runs on the server during SSR. Guard with import { isServer } from 'solid-js/web'; if (!isServer) { ... } or use onMount() for client-only effects.
For related framework issues, see Fix: SolidJS Not Working and Fix: TanStack Start Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Analog Not Working — Routes Not Loading, API Endpoints Failing, or Vite Build Errors
How to fix Analog (Angular meta-framework) issues — file-based routing, API routes with Nitro, content collections, server-side rendering, markdown pages, and deployment.
Fix: Angular SSR Not Working — Hydration Failing, Window Not Defined, or Build Errors
How to fix Angular Server-Side Rendering issues — @angular/ssr setup, hydration, platform detection, transfer state, route-level rendering, and deployment configuration.
Fix: Astro Actions Not Working — Form Submission Failing, Validation Errors Missing, or Return Type Wrong
How to fix Astro Actions issues — action definition, Zod validation, form handling, progressive enhancement, error handling, file uploads, and calling actions from client scripts.
Fix: TanStack Start Not Working — Server Functions Failing, Routes Not Loading, or SSR Errors
How to fix TanStack Start issues — project setup, file-based routing, server functions with createServerFn, data loading, middleware, SSR hydration, and deployment configuration.