From SolidStart
Released SolidStart 2 runs on Solid 1 and does not support Solid 2. Do not install Solid 2 into an existing SolidStart application.
Create a separate Solid 2 application that uses @solidjs/vite-plugin 3 start mode, then move application code into that target.
Start mode is a serving mode of the Vite plugin.
It is not SolidStart and does not provide the SolidStart runtime.
This guide has separate extraction steps for SolidStart 1 and released SolidStart 2. Both paths then use the same Solid 2 target model.
Migrate platform dependencies first
Apply the platform migrations as code moves into the separate Solid 2 target:
- Migrate core reactivity, lifecycle, boundaries, and rendering with From Solid 1.
- Migrate route definitions and router APIs with From Solid Router when the source uses Solid Router.
- Migrate head management with From Solid Meta 0.x when the source uses Solid Meta.
- Apply the SolidStart extraction steps on this page for entries, server functions, middleware, sessions, and deployment.
Do not copy Solid 1 component code into the target before checking its changed runtime APIs.
Keep the old application runnable during the migration. Move one route or server feature at a time, and compare its behavior in both applications before continuing.
Choose what to copy
Copy these files after checking their imports:
- Static files from
public/. - CSS, images, fonts, and other browser assets.
- Components whose imports are compatible with Solid 2.
- Domain code that does not import SolidStart, Vinxi, Nitro, or H3.
- Route component bodies after selecting a router and adapting their route exports.
- Server-function bodies after restoring authorization and validation at the new server boundary.
- Environment variable names and
.env.examplevalues that are not secrets.
Rewrite these integration files:
app.config.tsor a SolidStartvite.config.ts.src/entry-client.*andsrc/entry-server.*.- The document shell and application root.
- The router bootstrap and the SolidStart
FileRoutesusage. - Imports from
@solidjs/start/*,vinxi/*,nitropack/*, or Nitro and H3 runtime modules. - Middleware, session helpers, API handler types, deployment adapters, and provider configuration.
Do not copy a build output directory or a lockfile into the target. Install the target dependencies first, then add application dependencies as their callers move.
Start from SolidStart 1
SolidStart 1 uses Vinxi, Nitro, app.config.ts, and framework-owned entries.
Record the source behavior before changing files:
- Record the value of
ssr, theappRoot, the route directory, middleware paths, route prerendering, and every option underserverinapp.config.ts. - Record aliases supplied by the framework, including imports that start with
~/. - List custom code in
entry-client.*,entry-server.*, andapp.*. - List all imports from
@solidjs/start,vinxi,nitropack, and direct Nitro or H3 entrypoints. - List API routes, server functions, session operations, middleware hooks, and deployment presets.
- Run the old development server and production deployment tests as a behavioral baseline.
Do not migrate the application to released SolidStart 2 as an intermediate step. Move from the SolidStart 1 source directly into the separate Solid 2 target.
Apply these source-specific rules:
- Treat
app.config.tsas a checklist only. Do not copy its configuration object. - Split the SolidStart
app.*file into the targetsrc/App.tsxapplication tree andsrc/Document.tsxHTML shell. - Remove the old
StartClient,StartServer,mount, andcreateHandlerentry code. Generated start-mode entries replace it. - Preserve route filenames only when the selected file-system convention produces the same paths.
Rewrite the
FileRoutesintegration and route-level types. - Preserve the code inside a
"use server"function only after removing SolidStart runtime imports and checking its request, response, redirect, and serialization behavior. - Rewrite
onRequestandonBeforeResponsemiddleware as fetch-style middleware. - Replace
vinxi/httpsession and cookie calls with a session library over the Solid request event. - Replace Nitro presets, storage, tasks, WebSockets, and prerender options with host or Vite integrations selected for the new deployment.
Start from released SolidStart 2
Released SolidStart 2 uses Solid 1, solidStart(), Vite environment builds, and a deployment plugin such as Nitro.
Its Vite configuration resembles the target, but the application and server contracts are different.
Record the source behavior before changing files:
- Record every
solidStart()option, includingmiddleware,serialization,devOverlay, route locations, and rendering mode. - Record the Nitro, Netlify, Cloudflare, or other deployment plugin configuration.
- List custom behavior in
src/app.*,src/entry-client.*, andsrc/entry-server.*. - List imports from
@solidjs/start/config,@solidjs/start/client,@solidjs/start/server,@solidjs/start/http, and@solidjs/start/middleware. - List API routes, H3 middleware, session helpers, request-event locals, and server functions.
- Run development, build, preview, and deployment tests as a behavioral baseline.
Do not replace solidStart() with solid() in the existing application and assume that the result has the same runtime.
Create the separate target, then apply these source-specific rules:
- Remove
@solidjs/start,solidStart(), and the SolidStart environment type reference. - Remove the deployment plugin until the start-mode build works through
vite preview. Add a host integration only after the web-standard handler works. - Rewrite H3 middleware and
@solidjs/start/httpcalls againstRequest,Response, and the Solid request event. - Replace
StartClient,StartServer,createHandler, andFileRoutes. - Treat
serialization,devOverlay, islands, route prerendering, and Nitro configuration as features to redesign. Start mode has no matching application options for them. - Recheck all router code. Released SolidStart router versions and Solid 2 router versions do not have the same contracts.
Build the Solid 2 target
The current source baselines are @solidjs/vite-plugin at 519da14d and the solid-v2 templates at cbbd8ba2.
Use the closest template as the target, or create an empty Vite project with the same dependency and script shape.
Select a target before moving application files:
- Use
solid-v2/barefor a client-rendered application without server functions. - Use
solid-v2/basicfor Solid Router and file-system pages. - Use
solid-v2/fullstackfor streaming server-side rendering (SSR), server functions, sessions, middleware, and API routes. - Use
solid-v2/fullstack-tanstackwhen TanStack Router owns routing and TanStack Query owns the data cache.
The following checklist uses an SSR application with Solid Router.
Omit ssr: true only when the source application is client-rendered.
Omit serverFunctions and middleware when the application has no server behavior.
Create the package boundary
Start from the selected Solid 2 template's package.json.
Keep the template's compatible versions of Solid 2, @solidjs/web, Vite, and @solidjs/vite-plugin.
Do not carry @solidjs/start, Vinxi, Nitro, or an old Solid Router release into the target.
Use Vite scripts:
{ "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }}Copy application dependencies only after checking that each package supports Solid 2. Replace a package when it imports Solid 1 internals or requires SolidStart runtime modules.
Configure start mode
Add @solidjs/vite-plugin start mode in vite.config.ts.
The start option owns entries, development serving, preview serving, and production build wiring.
The separate ssr boolean selects streaming SSR.
import { fileRoutes } from "filesystem-routing/vite";import { defineConfig } from "vite";import solid from "@solidjs/vite-plugin";
export default defineConfig({ plugins: [ solid({ start: { middleware: "./src/middleware.ts", }, ssr: true, serverFunctions: true, extensions: [".jsx", ".tsx"], }), fileRoutes({ httpMethods: true }), ],});start: true and start: {} enable the same mode.
Use the object form only when the application needs app, entryServer, entryClient, document, middleware, setup, env, or external.
The extensions entry makes the Solid transform accept route module IDs generated with query suffixes by filesystem-routing.
Map old configuration deliberately:
- Map source SSR to the top-level
ssrboolean. - Map the app root to
start.apponly when the target does not use thesrc/App.*convention. - Map the route directory to
fileRoutes({ dir }), not tostart. - Map middleware to
start.middleware. - Add Vite aliases under
resolve.alias; start mode does not add the SolidStart~alias. - Move host build and serving ownership behind the deployment boundary.
Do not place Nitro options in
start.
See the StartOptions reference for the complete option contracts.
Let the plugin generate entries
Delete or move the SolidStart src/entry-client.* and src/entry-server.* files before the first target build.
If those conventional files remain, start mode selects them instead of generating entries.
SolidStart entry modules cannot serve as start-mode entries because their imports and exports use different contracts.
Generated SSR entries:
- Render
<Document><App /></Document>on the server. - Hydrate the same tree in the browser.
- Inject the built client entry and styles.
The start-mode handler creates the request event used by middleware, page rendering, and server functions.
Keep generated entries unless the old entries contain behavior that cannot move into App, Document, middleware, start.setup, or a server function.
When authored entries are necessary, rewrite both files as a pair.
The server entry must export render(request?, context?), and the client entry must hydrate the same document tree.
Split App.tsx and Document.tsx
Create src/App.tsx as the default-exporting application root.
Move the router, providers, layouts, and application UI here.
Do not put <html>, <head>, or <body> in App.
import { Router } from "./router";
export default function App() { return <Router>{(props) => props.children}</Router>;}Create src/Document.tsx as the default-exporting HTML shell.
Move language, meta, icon, and document-level tags here.
Render props.children in the body and include <HydrationScript /> for SSR.
import type { ParentProps } from "solid-js";import { HydrationScript } from "@solidjs/web";
export default function Document(props: ParentProps) { return ( <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <HydrationScript /> </head> <body>{props.children}</body> </html> );}The generated entry injects its client script into the document head.
Do not copy SolidStart props.assets, props.scripts, or an #app mount wrapper into this component.
Select and mount a router
Start mode does not select a router or discover pages. Choose the router before copying route files.
For Solid Router:
- Add
filesystem-routingand its Vite plugin when file-based routes are required. - Convert
pageRouteswithfileRoutes()from@solidjs/router/fs. - Create one router and render it under
App. - Rewrite old
FileRoutesimports and route configuration types.
import { pageRoutes } from "virtual:file-routes";import { createRouter } from "@solidjs/router";import { fileRoutes } from "@solidjs/router/fs";
export const Router = createRouter({ routes: fileRoutes(pageRoutes),});The default nested file convention preserves common paths such as:
routes/index.tsxto/.routes/blog/index.tsxto/blog/.routes/users/[id].tsxto/users/:id.routes/docs/[...path].tsxto/docs/*path.
Pairing routes/users.tsx with routes/users/ creates a layout and nested pages.
Route groups remain available, but compare every generated path before moving the next route.
See the file-system route conventions and Solid Router route conversion.
For TanStack Router, start from the pinned fullstack-tanstack template.
Its router plugin owns src/routes and generates routeTree.gen.ts.
For SSR, start.setup creates and loads a request-specific router before the generated entry renders.
Do not reuse SolidStart's router server or client entry protocol.
See the TanStack Router integration.
Move route modules incrementally
Move one layout and its child routes at a time. For each route:
- Confirm the generated path.
- Copy the component body.
- Rewrite router imports, route props, preload functions, search validation, and redirects.
- Replace SolidStart metadata or data APIs with APIs from the selected router and
@solidjs/meta. - Test direct SSR, client navigation, refresh, parameters, pending UI, errors, and
404status.
Do not copy FileRoutes or assume a SolidStart route export has the same type in Solid Router 2 or TanStack Solid Router 1.
The route component is usually reusable.
The route registration and data boundary usually require changes.
The data boundary is where most SolidStart 1 route code changes.
createAsync(() => getUser(params.id)) becomes createMemo(() => getUser(params.id)), a route load that returned data becomes a preload that starts the query with void and lets the component read it, and useSubmission(action).pending splits into aria-busy on the form, an optimistic store, and useSubmissions for errors.
Migrate from Solid Router 1 shows each before and after, and Data loading and mutations teaches the resulting shape.
Move server functions
Enable serverFunctions: true before moving any "use server" functions.
The default endpoint remains /_server.
Function-level and module-level directives are supported:
export async function findUser(id: string) { "use server"; return database.users.find(id);}Copy the implementation only after applying these checks:
- Keep database and credential imports in server-only modules.
- Replace SolidStart HTTP helpers with web
Request,Response, andgetRequestEvent()from@solidjs/web. - Validate arguments and authorization inside every callable function.
- Retest values that depended on SolidStart serialization.
- Replace old
queryandactionwrappers with the selected router's Solid 2 APIs. - Replace response helpers with the current
@solidjs/webor router contract.
Apply these server-function API replacements:
- Import
GETfrom@solidjs/web/server-functionsinstead of@solidjs/start. - Replace a
.GETproxy property withGET(fn). - Replace
.withOptions(init)session policy withconfigureServerFunctionsClient({ prepareRequest }). - Build custom codec plugins with
createPluginfrom@solidjs/web/serialization, then pass matchingcodecoptions to the client and server runtime. - Remove manually attached single-flight headers. The router opts in by registering its flight-data consumer.
Server functions are transport primitives in the plugin. Router caching, submissions, revalidation, and single-flight behavior require the matching router integration. See Server functions.
Rebuild environment handling
Vite public variables that use the configured VITE_ prefix remain available through import.meta.env.
Keep secrets in server-only modules.
Start mode also supports a typed Standard Schema file at env.ts or env.js in the project root:
Import validated values from virtual:env/server in server-only modules and from virtual:env/client in shared or client code.
The plugin generates solid-env.d.ts next to the schema.
Do not copy a SolidStart @solidjs/start/env type reference.
The server module reads and validates server values from process.env when the server starts.
Client values are validated and embedded in the browser bundle.
Copy .env.example, but do not copy local secrets into source control.
See Environment.
Rewrite sessions and authentication
Start mode supplies a request event, cookies on web headers, and response metadata. It does not supply SolidStart session helpers or an authentication system.
Rewrite calls to useSession, getSession, setCookie, and related SolidStart, Vinxi, or H3 helpers.
The official fullstack template composes @remix-run/cookie with:
getRequestEvent()from@solidjs/web.- The incoming
event.request.headers. - Outgoing
Set-Cookievalues appended toevent.response.headers. - A secret read from
virtual:env/server.
That template cookie is signed and tamper-evident, but it is not encrypted. Do not put secrets in its payload. Use a storage-backed session library when the application requires revocation, larger data, or server-owned session state.
Keep authorization inside every protected server function and API handler.
Middleware can populate event.locals, but hiding UI or checking a page request does not authorize another server entry point.
See Sessions and auth.
Rewrite middleware and API routes
Configure a server-only module through start.middleware.
Its default export is one fetch-style middleware function or an array:
type Middleware = ( request: Request, next: (request?: Request) => Response | Promise<Response>) => Response | Promise<Response>;Code before await next() runs in declaration order.
Code after it runs while the chain unwinds.
Return a Response without calling next() to stop dispatch.
Use getRequestEvent() inside the chain to read or write request-scoped locals and response metadata.
API route discovery is a separate filesystem-routing integration.
Enable fileRoutes({ httpMethods: true }), then place its dispatcher in the middleware chain:
import { createAPIHandler } from "filesystem-routing/api";import routes from "virtual:file-routes";
export default [createAPIHandler(routes)];An API module exports uppercase HTTP methods:
import type { APIHandler } from "filesystem-routing/api";
export const GET: APIHandler = ({ request, params }) => { return Response.json({ path: new URL(request.url).pathname, params, });};A method-only module is an API route without a page.
A module can also default-export a page and export methods.
The handler result and fallback rules are not identical to SolidStart, so retest HEAD, missing methods, undefined results, route parameters, cookies, and errors.
See Middleware and API routes.
Rebuild the deployment boundary
Run vite build before adding provider-specific integration.
With start and ssr: true, the build writes:
- Browser assets to
dist/client. - A server module to
dist/server/server.js.
The server module exports handleRequest.
Its contract is a web Request in and a promised web Response out:
import { handleRequest } from "./dist/server/server.js";
const response = await handleRequest(request);Serve dist/client before passing unmatched requests to the handler.
Preserve the request URL, method, headers, and body.
Preserve the response status, headers, separate Set-Cookie values, and streamed body.
Do not deploy the old Nitro output or reuse a SolidStart provider preset.
For Node, set start: { node: true }; the build then writes dist/server/node.js, a server that implements this boundary and runs with node dist/server/node.js.
Other hosts need an adapter or Vite integration that implements this exact asset and handler boundary.
Set start.external: true only when a host integration owns both server build wiring and HTTP serving.
The plugin continues to provide generated entries, the client manifest, and its virtual handler.
See Deployment.
Resolve removed framework features
Start mode does not replace every SolidStart feature. Resolve each old dependency explicitly:
- Replace the
~alias with a Vite alias or relative imports. - Replace
appRootand route directory behavior with plugin paths andfileRoutes({ dir }). - Replace the SolidStart development toolbar with Vite and browser tooling.
- Replace framework route prerendering with a host or build integration.
- Replace Nitro tasks, storage, WebSockets, and deployment presets with runtime-specific services.
- Replace islands and other SolidStart rendering modes with a supported Solid 2 application design.
- Retest values that depended on SolidStart's
serializationoption against the Solid 2 server-function codec. - Replace
@solidjs/start/httpconvenience helpers with web APIs,@solidjs/web, or focused libraries. - Replace framework API route dispatch with
filesystem-routingmiddleware or another server integration.
Keep an item unresolved when its target host has no verified Solid 2 adapter.
Do not hide a host-specific gap behind start.external.
Verify each migration slice
Run these checks after every route group or server feature moves:
- Open the route directly in a new browser request.
- Navigate to and from the route in the browser.
- Compare the rendered document head and status code.
- Exercise server-function success, validation failure, authorization failure, and redirects.
- Exercise API methods, middleware order, cookies, and session expiry.
- Run
vite buildandvite preview. - Run the production adapter with streamed responses and multiple
Set-Cookieheaders. - Check the client output for server-only modules and secret values.
- Remove the migrated source feature only after the target passes the same behavioral tests.
Finish by testing the actual deployment host. Preview verifies the generated artifacts, but it does not verify provider routing, environment injection, asset rules, streaming, or cookie forwarding.