diff --git a/.github/labeler.yml b/.github/labeler.yml
index 650e4e34d1..65e3cde23b 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -22,20 +22,14 @@
- 'packages/start-api-routes/**/*'
'package: react-start-client':
- 'packages/react-start-client/**/*'
-'package: react-start-config':
- - 'packages/react-start-config/**/*'
'package: react-start-plugin':
- 'packages/react-start-plugin/**/*'
-'package: react-start-router-manifest':
- - 'packages/react-start-router-manifest/**/*'
'package: react-start-server':
- 'packages/react-start-server/**/*'
'package: start-server-functions-client':
- 'packages/start-server-functions-client/**/*'
'package: start-server-functions-fetcher':
- 'packages/start-server-functions-fetcher/**/*'
-'package: start-server-functions-handler':
- - 'packages/start-server-functions-handler/**/*'
'package: start-server-functions-ssr':
- 'packages/start-server-functions-ssr/**/*'
'package: router-cli':
@@ -74,8 +68,6 @@
- 'packages/solid-start-server/**/*'
'package: start':
- 'packages/start/**/*'
-'package: start-config':
- - 'packages/start-config/**/*'
'package: start-server-functions-server':
- 'packages/start-server-functions-server/**/*'
'package: valibot-adapter':
diff --git a/.gitignore b/.gitignore
index 5cc3e94113..36785455d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,12 +61,14 @@ nx-cloud.env
gpt/db.json
app.config.timestamp-*
+vite.config.timestamp-*
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
app.config.js.timestamp-*
app.config.ts.timestamp-*
app.config.timestamp_*
+vite.config.timestamp_*
vite.config.js.timestamp_*
vite.config.ts.timestamp_*
app.config.js.timestamp_*
diff --git a/.npmrc b/.npmrc
index 84aee8d998..ad30a6c4d2 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,3 +1,3 @@
link-workspace-packages=true
prefer-workspace-packages=true
-provenance=true
+provenance=false
diff --git a/.prettierignore b/.prettierignore
index d285f8937d..25c93015ed 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -7,7 +7,6 @@
pnpm-lock.yaml
**/snapshots
**/.vercel
-**/.vinxi
**/.output
**/node_modules
node_modules
diff --git a/docs/router/eslint/create-route-property-order.md b/docs/router/eslint/create-route-property-order.md
index 3ce5102013..c0fee218d2 100644
--- a/docs/router/eslint/create-route-property-order.md
+++ b/docs/router/eslint/create-route-property-order.md
@@ -29,7 +29,7 @@ Examples of **incorrect** code for this rule:
/* eslint "@tanstack/router/create-route-property-order": "warn" */
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/foo/bar/$id')({
+export const Route = createFileRoute({
loader: async ({context}) => {
await context.queryClient.ensureQueryData(getQueryOptions(context.hello)),
},
@@ -43,7 +43,7 @@ Examples of **correct** code for this rule:
/* eslint "@tanstack/router/create-route-property-order": "warn" */
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/foo/bar/$id')({
+export const Route = createFileRoute({
beforeLoad: () => ({hello: 'world'}),
loader: async ({context}) => {
await context.queryClient.ensureQueryData(getQueryOptions(context.hello)),
diff --git a/docs/router/framework/react/api/router/createFileRouteFunction.md b/docs/router/framework/react/api/router/createFileRouteFunction.md
index 945e713075..0986d6ccff 100644
--- a/docs/router/framework/react/api/router/createFileRouteFunction.md
+++ b/docs/router/framework/react/api/router/createFileRouteFunction.md
@@ -26,7 +26,7 @@ A new function that accepts a single argument of type [`RouteOptions`](./RouteOp
```tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/')({
+export const Route = createFileRoute({
loader: () => {
return 'Hello World'
},
diff --git a/docs/router/framework/react/api/router/retainSearchParamsFunction.md b/docs/router/framework/react/api/router/retainSearchParamsFunction.md
index 97e411b29c..d4bc185203 100644
--- a/docs/router/framework/react/api/router/retainSearchParamsFunction.md
+++ b/docs/router/framework/react/api/router/retainSearchParamsFunction.md
@@ -39,7 +39,7 @@ const searchSchema = z.object({
two: z.string().optional(),
})
-export const Route = createFileRoute('/hello')({
+export const Route = createFileRoute({
validateSearch: zodValidator(searchSchema),
search: {
middlewares: [retainSearchParams(true)],
diff --git a/docs/router/framework/react/api/router/stripSearchParamsFunction.md b/docs/router/framework/react/api/router/stripSearchParamsFunction.md
index 05b9b02e7f..439a1796d2 100644
--- a/docs/router/framework/react/api/router/stripSearchParamsFunction.md
+++ b/docs/router/framework/react/api/router/stripSearchParamsFunction.md
@@ -30,7 +30,7 @@ const searchSchema = z.object({
two: z.string().default(defaultValues.two),
})
-export const Route = createFileRoute('/hello')({
+export const Route = createFileRoute({
validateSearch: zodValidator(searchSchema),
search: {
// strip default values
@@ -68,7 +68,7 @@ const searchSchema = z.object({
two: z.string().default('xyz'),
})
-export const Route = createFileRoute('/hello')({
+export const Route = createFileRoute({
validateSearch: zodValidator(searchSchema),
search: {
// remove all search params
diff --git a/docs/router/framework/react/decisions-on-dx.md b/docs/router/framework/react/decisions-on-dx.md
index 405db4c6eb..a38716f624 100644
--- a/docs/router/framework/react/decisions-on-dx.md
+++ b/docs/router/framework/react/decisions-on-dx.md
@@ -225,7 +225,7 @@ Let's take a look at how the route configuration for the previous example would
// src/routes/posts/index.ts
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts/')({
+export const Route = createFileRoute({
component: () => 'Posts index component goes here!!!',
})
```
diff --git a/docs/router/framework/react/guide/external-data-loading.md b/docs/router/framework/react/guide/external-data-loading.md
index e2d0e1eb53..2b1ebf78f1 100644
--- a/docs/router/framework/react/guide/external-data-loading.md
+++ b/docs/router/framework/react/guide/external-data-loading.md
@@ -48,7 +48,7 @@ Here is a naive illustration (don't do this) of using a Route's `loader` option
// src/routes/posts.tsx
let postsCache = []
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
loader: async () => {
postsCache = await fetchPosts()
},
@@ -80,7 +80,7 @@ const postsQueryOptions = queryOptions({
queryFn: () => fetchPosts(),
})
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
// Use the `loader` option to ensure that the data is loaded
loader: () => queryClient.ensureQueryData(postsQueryOptions),
component: () => {
@@ -105,7 +105,7 @@ export const Route = createFileRoute('/posts')({
When an error occurs while using `suspense` with `Tanstack Query`, you'll need to let queries know that you want to try again when re-rendering. This can be done by using the `reset` function provided by the `useQueryErrorResetBoundary` hook. We can invoke this function in an effect as soon as the error component mounts. This will make sure that the query is reset and will try to fetch data again when the route component is rendered again. This will also cover cases where users navigate away from our route instead of clicking the `retry` button.
```tsx
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
loader: () => queryClient.ensureQueryData(postsQueryOptions),
errorComponent: ({ error, reset }) => {
const router = useRouter()
diff --git a/docs/router/framework/react/guide/router-context.md b/docs/router/framework/react/guide/router-context.md
index 561b1e2f00..f54e7a7b23 100644
--- a/docs/router/framework/react/guide/router-context.md
+++ b/docs/router/framework/react/guide/router-context.md
@@ -92,7 +92,7 @@ Once you have defined the router context type, you can use it in your route defi
```tsx
// src/routes/todos.tsx
-export const Route = createFileRoute('/todos')({
+export const Route = createFileRoute({
component: Todos,
loader: ({ context }) => fetchTodosByUserId(context.user.id),
})
@@ -122,7 +122,7 @@ Then, in your route:
```tsx
// src/routes/todos.tsx
-export const Route = createFileRoute('/todos')({
+export const Route = createFileRoute({
component: Todos,
loader: ({ context }) => context.fetchTodosByUserId(context.userId),
})
@@ -158,7 +158,7 @@ Then, in your route:
```tsx
// src/routes/todos.tsx
-export const Route = createFileRoute('/todos')({
+export const Route = createFileRoute({
component: Todos,
loader: async ({ context }) => {
await context.queryClient.ensureQueryData({
@@ -234,7 +234,7 @@ So, now in our route's `loader` function, we can access the `networkStrength` ho
```tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
component: Posts,
loader: ({ context }) => {
if (context.networkStrength === 'STRONG') {
@@ -282,7 +282,7 @@ const router = createRouter({
```tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/todos')({
+export const Route = createFileRoute({
component: Todos,
beforeLoad: () => {
return {
diff --git a/docs/router/framework/react/guide/tanstack-start.md b/docs/router/framework/react/guide/tanstack-start.md
index 9704d721c4..a56a204e28 100644
--- a/docs/router/framework/react/guide/tanstack-start.md
+++ b/docs/router/framework/react/guide/tanstack-start.md
@@ -47,15 +47,12 @@ TanStack Start is powered by the following packages and need to be installed as
- [@tanstack/start](https://github.com/tanstack/start)
- [@tanstack/react-router](https://tanstack.com/router)
-- [Vinxi](https://vinxi.vercel.app/)
-
-> [!NOTE]
-> Vinxi is a temporary dependency that will be replaced by a simple vite plugin or a dedicated Start CLI.
+- [Vite](https://vite.dev/)
To install them, run:
```shell
-npm i @tanstack/react-start @tanstack/react-router vinxi
+npm i @tanstack/react-start @tanstack/react-router vite
```
You'll also need React and the Vite React plugin, so install their dependencies as well:
@@ -72,16 +69,16 @@ npm i -D typescript @types/react @types/react-dom
# Update Configuration Files
-We'll then update our `package.json` to use Vinxi's CLI and set `"type": "module"`:
+We'll then update our `package.json` to use Vite's CLI and set `"type": "module"`:
```jsonc
{
// ...
"type": "module",
"scripts": {
- "dev": "vinxi dev",
- "build": "vinxi build",
- "start": "vinxi start",
+ "dev": "vite dev",
+ "build": "vite build",
+ "start": "vite start",
},
}
```
@@ -97,12 +94,10 @@ export default defineConfig({})
# Add the Basic Templating
-There are four required files for TanStack Start usage:
+There are 2 required files for TanStack Start usage:
1. The router configuration
-2. The server entry point
-3. The client entry point
-4. The root of your application
+2. The root of your application
Once configuration is done, we'll have a file tree that looks like the following:
@@ -111,10 +106,8 @@ Once configuration is done, we'll have a file tree that looks like the following
├── app/
│ ├── routes/
│ │ └── `__root.tsx`
-│ ├── `client.tsx`
│ ├── `router.tsx`
│ ├── `routeTree.gen.ts`
-│ └── `ssr.tsx`
├── `.gitignore`
├── `app.config.ts`
├── `package.json`
@@ -123,7 +116,7 @@ Once configuration is done, we'll have a file tree that looks like the following
## The Router Configuration
-This is the file that will dictate the behavior of TanStack Router used within Start. Here, you can configure everything
+This is the file that will dictate the behavior of TanStack Router used within Start for both the server and the client. Here, you can configure everything
from the default [preloading functionality](./preloading.md) to [caching staleness](./data-loading.md).
```tsx
@@ -149,52 +142,9 @@ declare module '@tanstack/react-router' {
> `routeTree.gen.ts` is not a file you're expected to have at this point.
> It will be generated when you run TanStack Start (via `npm run dev` or `npm run start`) for the first time.
-## The Server Entry Point
-
-As TanStack Start is an [SSR](https://unicorn-utterances.com/posts/what-is-ssr-and-ssg) framework, we need to pipe this router
-information to our server entry point:
-
-```tsx
-// app/ssr.tsx
-import {
- createStartHandler,
- defaultStreamHandler,
-} from '@tanstack/react-start/server'
-import { getRouterManifest } from '@tanstack/react-start/router-manifest'
-
-import { createRouter } from './router'
-
-export default createStartHandler({
- createRouter,
- getRouterManifest,
-})(defaultStreamHandler)
-```
-
-This allows us to know what routes and loaders we need to execute when the user hits a given route.
-
-## The Client Entry Point
-
-Now we need a way to hydrate our client-side JavaScript once the route resolves to the client. We do this by piping the same
-router information to our client entry point:
-
-```tsx
-// app/client.tsx
-import { hydrateRoot } from 'react-dom/client'
-import { StartClient } from '@tanstack/react-start'
-import { createRouter } from './router'
-
-const router = createRouter({
- scrollRestoration: true,
-})
-
-hydrateRoot(document!, )
-```
-
-This enables us to kick off client-side routing once the user's initial server request has fulfilled.
-
## The Root of Your Application
-Finally, we need to create the root of our application. This is the entry point for all other routes. The code in this file will wrap all other routes in the application.
+Finally, we need to create the root of our application. This is the entry point for all application routes. The code in this file will wrap all other routes in the application.
```tsx
// app/routes/__root.tsx
diff --git a/docs/router/framework/react/migrate-from-react-location.md b/docs/router/framework/react/migrate-from-react-location.md
index 100d614cae..fc04391c61 100644
--- a/docs/router/framework/react/migrate-from-react-location.md
+++ b/docs/router/framework/react/migrate-from-react-location.md
@@ -112,7 +112,7 @@ export const Route = createRootRoute({
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/')({
+export const Route = createFileRoute({
component: Index,
})
```
@@ -125,7 +125,7 @@ export const Route = createFileRoute('/')({
// src/routes/posts.tsx
import { createFileRoute, Link, Outlet } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
component: Posts,
loader: async () => {
const posts = await fetchPosts()
@@ -164,7 +164,7 @@ function Posts() {
// src/routes/posts.index.tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts/')({
+export const Route = createFileRoute({
component: PostsIndex,
})
```
@@ -177,7 +177,7 @@ export const Route = createFileRoute('/posts/')({
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts/$postId')({
+export const Route = createFileRoute({
component: PostsId,
loader: async ({ params: { postId } }) => {
const post = await fetchPost(postId)
diff --git a/docs/router/framework/react/overview.md b/docs/router/framework/react/overview.md
index be83513a79..9e064cf0a1 100644
--- a/docs/router/framework/react/overview.md
+++ b/docs/router/framework/react/overview.md
@@ -42,7 +42,7 @@ It's probably no surprise at this point that picking a router is so important th
**Does this mean that TanStack Router is a framework?**
-TanStack Router itself is not a "framework" in the traditional sense, since it doesn't address a few other common full-stack concerns. However TanStack Router has been designed to be upgradable to a full-stack framework when used in conjunction with other tools that address bundling, deployments, and server-side-specific functionality. This is why we are currently developing [TanStack Start](https://tanstack.com/start), a full-stack framework that is built on top of TanStack Router and tools like Nitro, and Vite.
+TanStack Router itself is not a "framework" in the traditional sense, since it doesn't address a few other common full-stack concerns. However TanStack Router has been designed to be upgradable to a full-stack framework when used in conjunction with other tools that address bundling, deployments, and server-side-specific functionality. This is why we are currently developing [TanStack Start](https://tanstack.com/start), a full-stack framework that is built on top of TanStack Router and Vite.
For a deeper dive on the history of TanStack Router, feel free to read [TanStack Router's History](./decisions-on-dx.md#tanstack-routers-origin-story).
diff --git a/docs/router/framework/react/routing/routing-concepts.md b/docs/router/framework/react/routing/routing-concepts.md
index f07bb65868..c6a21c3c92 100644
--- a/docs/router/framework/react/routing/routing-concepts.md
+++ b/docs/router/framework/react/routing/routing-concepts.md
@@ -13,7 +13,7 @@ All other routes, other than the [Root Route](#the-root-route), are configured u
```tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts')({
+export const Route = createFileRoute({
component: PostsComponent,
})
```
@@ -71,7 +71,7 @@ Let's take a look at an `/about` route:
// about.tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/about')({
+export const Route = createFileRoute({
component: AboutComponent,
})
@@ -93,7 +93,7 @@ Let's take a look at an index route for a `/posts` URL:
import { createFileRoute } from '@tanstack/react-router'
// Note the trailing slash, which is used to target index routes
-export const Route = createFileRoute('/posts/')({
+export const Route = createFileRoute({
component: PostsIndexComponent,
})
@@ -113,7 +113,7 @@ These params are then usable in your route's configuration and components! Let's
```tsx
import { createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/posts/$postId')({
+export const Route = createFileRoute({
// In a loader
loader: ({ params }) => fetchPost(params.postId),
// Or in a component
@@ -172,7 +172,7 @@ This tree structure is used to wrap the child routes with a layout component:
```tsx
import { Outlet, createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/app')({
+export const Route = createFileRoute({
component: AppLayoutComponent,
})
@@ -243,7 +243,7 @@ The `_pathlessLayout.tsx` route is used to wrap the child routes with a Pathless
```tsx
import { Outlet, createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/_pathlessLayout')({
+export const Route = createFileRoute({
component: PathlessLayoutComponent,
})
diff --git a/docs/start/config.json b/docs/start/config.json
index 5aa7af5324..50cf495a70 100644
--- a/docs/start/config.json
+++ b/docs/start/config.json
@@ -46,8 +46,8 @@
"to": "framework/react/middleware"
},
{
- "label": "API Routes",
- "to": "framework/react/api-routes"
+ "label": "Server Routes",
+ "to": "framework/react/server-routes"
},
{
"label": "SSR",
diff --git a/docs/start/framework/react/api-routes.md b/docs/start/framework/react/api-routes.md
deleted file mode 100644
index a1d1dd6d4c..0000000000
--- a/docs/start/framework/react/api-routes.md
+++ /dev/null
@@ -1,292 +0,0 @@
----
-id: api-routes
-title: API Routes
----
-
-API Routes are a powerful feature of TanStack Start that allow you to create server-side endpoints in your application without the need for a separate server. API Routes are useful for handling form submissions, user authentication, and more.
-
-By default, API Routes are defined in your `./app/routes/api` directory of your project and are automatically handled by the TanStack Start server.
-
-> 🧠 This means that by default, your API Routes will be prefixed with `/api` and will be served from the same server as your application. You can customize this base path by changing the `server.apiBaseURL` in your TanStack Start config.
-
-## File Route Conventions
-
-API Routes in TanStack Start, follow the same file-based routing conventions as TanStack Router. This means that each file in your `routes` directory that is prefixed with `api` (which can be configured) will be treated as an API route. Here are a few examples:
-
-- `routes/api.users.ts` will create an API route at `/api/users`
-- `routes/api/users.ts` will **also** create an API route at `/api/users`
-- `routes/api/users.index.ts` will **also** create an API route at `/api/users`
-- `routes/api/users/$id.ts` will create an API route at `/api/users/$id`
-- `routes/api/users/$id/posts.ts` will create an API route at `/api/users/$id/posts`
-- `routes/api.users.$id.posts.ts` will **also** create an API route at `/api/users/$id/posts`
-- `routes/api/file/$.ts` will create an API route at `/api/file/$`
-
-Your route files that are prefixed with `api`, can be thought of as the handlers for the given API route path.
-
-It's important to remember that each route can only have a single handler file associated with it. So, if you have a file named `routes/api/users.ts` which'd equal the request path of `/api/users`, you cannot have other files that'd also resolve to the same route, like:
-
-- `routes/api/users.index.ts`
-- `routes/api.users.ts`.
-- `routes/api.users.index.ts`.
-
-❗ One more thing, API Routes do not have the concept of pathless layout routes or parallel routes. So, a file named:
-
-- `routes/api/_pathlessLayout/users.ts` would resolve to `/api/_pathlessLayout/users` and **NOT** `/api/users`.
-
-## Nested Directories vs File-names
-
-In the examples above, you may have noticed that the file naming conventions are flexible and allow you to mix and match directories and file names. This is intentional and allows you to organize your API Routes in a way that makes sense for your application. You can read more about this in the [TanStack Router File-based Routing Guide](/router/latest/docs/framework/react/routing/file-based-routing#s-or-s).
-
-## Setting up the entry handler
-
-Before you can create your API routes, you need to set up the entry handler for your TanStack Start project. This entry handler, similar to `client` and `ssr`, handles the API incoming requests and routes them to the appropriate API route handler. The API entry handler is defined in the `app/api.ts` file in your project.
-
-Here's an example implementation:
-
-```ts
-// app/api.ts
-import {
- createStartAPIHandler,
- defaultAPIFileRouteHandler,
-} from '@tanstack/react-start/api'
-
-export default createStartAPIHandler(defaultAPIFileRouteHandler)
-```
-
-This file is responsible for creating the API handler that will be used to route incoming requests to the appropriate API route handler. The `defaultAPIFileRouteHandler` is a helper function that will automatically load and execute the appropriate API route handler based on the incoming request.
-
-## Defining an API Route
-
-API Routes export an APIRoute instance by calling the `createAPIFileRoute` function. Similar to other file-based routes in TanStack Router, the first argument to this function is the path of the route. The function returned is called again with an object that defines the route handlers for each HTTP method.
-
-> [!TIP]
-> If you've already got the dev server running, when you create a new API route, it'll automatically have the initial handler set up for you. From there on, you can customize the handler as needed.
-
-> [!NOTE]
-> The export variable must be named `APIRoute` or the resulting response will be a `404 not found`.
-
-```ts
-// routes/api/hello.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/api/hello')({
- GET: async ({ request }) => {
- return new Response('Hello, World! from ' + request.url)
- },
-})
-```
-
-Each HTTP method handler receives an object with the following properties:
-
-- `request`: The incoming request object. You can read more about the `Request` object in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Request).
-- `params`: An object containing the dynamic path parameters of the route. For example, if the route path is `/users/$id`, and the request is made to `/users/123`, then `params` will be `{ id: '123' }`. We'll cover dynamic path parameters and wildcard parameters later in this guide.
-
-Once you've processed the request, you need to return a `Response` object or `Promise`. This can be done by creating a new `Response` object and returning it from the handler. You can read more about the `Response` object in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Response).
-
-## Dynamic Path Params
-
-API Routes support dynamic path parameters, which are denoted by a `$` followed by the parameter name. For example, a file named `routes/api/users/$id.ts` will create an API route at `/api/users/$id` that accepts a dynamic `id` parameter.
-
-```ts
-// routes/api/users/$id.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/users/$id')({
- GET: async ({ params }) => {
- const { id } = params
- return new Response(`User ID: ${id}`)
- },
-})
-
-// Visit /api/users/123 to see the response
-// User ID: 123
-```
-
-You can also have multiple dynamic path parameters in a single route. For example, a file named `routes/api/users/$id/posts/$postId.ts` will create an API route at `/api/users/$id/posts/$postId` that accepts two dynamic parameters.
-
-```ts
-// routes/api/users/$id/posts/$postId.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/users/$id/posts/$postId')({
- GET: async ({ params }) => {
- const { id, postId } = params
- return new Response(`User ID: ${id}, Post ID: ${postId}`)
- },
-})
-
-// Visit /api/users/123/posts/456 to see the response
-// User ID: 123, Post ID: 456
-```
-
-## Wildcard/Splat Param
-
-API Routes also support wildcard parameters at the end of the path, which are denoted by a `$` followed by nothing. For example, a file named `routes/api/file/$.ts` will create an API route at `/api/file/$` that accepts a wildcard parameter.
-
-```ts
-// routes/api/file/$.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/file/$')({
- GET: async ({ params }) => {
- const { _splat } = params
- return new Response(`File: ${_splat}`)
- },
-})
-
-// Visit /api/file/hello.txt to see the response
-// File: hello.txt
-```
-
-## Handling requests with a body
-
-To handle POST requests,you can add a `POST` handler to the route object. The handler will receive the request object as the first argument, and you can access the request body using the `request.json()` method.
-
-```ts
-// routes/api/hello.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/api/hello')({
- POST: async ({ request }) => {
- const body = await request.json()
- return new Response(`Hello, ${body.name}!`)
- },
-})
-
-// Send a POST request to /api/hello with a JSON body like { "name": "Tanner" }
-// Hello, Tanner!
-```
-
-This also applies to other HTTP methods like `PUT`, `PATCH`, and `DELETE`. You can add handlers for these methods in the route object and access the request body using the appropriate method.
-
-It's important to remember that the `request.json()` method returns a `Promise` that resolves to the parsed JSON body of the request. You need to `await` the result to access the body.
-
-This is a common pattern for handling POST requests in API Routes. You can also use other methods like `request.text()` or `request.formData()` to access the body of the request.
-
-## Responding with JSON
-
-When returning JSON using a Response object, this is a common pattern:
-
-```ts
-// routes/api/hello.ts
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/api/hello')({
- GET: async ({ request }) => {
- return new Response(JSON.stringify({ message: 'Hello, World!' }), {
- headers: {
- 'Content-Type': 'application/json',
- },
- })
- },
-})
-
-// Visit /api/hello to see the response
-// {"message":"Hello, World!"}
-```
-
-## Using the `json` helper function
-
-Or you can use the `json` helper function to automatically set the `Content-Type` header to `application/json` and serialize the JSON object for you.
-
-```ts
-// routes/api/hello.ts
-import { json } from '@tanstack/react-start'
-import { createAPIFileRoute } from '@tanstack/react-start/api'
-
-export const APIRoute = createAPIFileRoute('/api/hello')({
- GET: async ({ request }) => {
- return json({ message: 'Hello, World!' })
- },
-})
-
-// Visit /api/hello to see the response
-// {"message":"Hello, World!"}
-```
-
-## Responding with a status code
-
-You can set the status code of the response by either:
-
-- Passing it as a property of the second argument to the `Response` constructor
-
- ```ts
- // routes/api/hello.ts
- import { json } from '@tanstack/react-start'
- import { createAPIFileRoute } from '@tanstack/react-start/api'
-
- export const APIRoute = createAPIFileRoute('/users/$id')({
- GET: async ({ request, params }) => {
- const user = await findUser(params.id)
- if (!user) {
- return new Response('User not found', {
- status: 404,
- })
- }
- return json(user)
- },
- })
- ```
-
-- Using the `setResponseStatus` helper function from `@tanstack/react-start/server`
-
- ```ts
- // routes/api/hello.ts
- import { json } from '@tanstack/react-start'
- import { createAPIFileRoute } from '@tanstack/react-start/api'
- import { setResponseStatus } from '@tanstack/react-start/server'
-
- export const APIRoute = createAPIFileRoute('/users/$id')({
- GET: async ({ request, params }) => {
- const user = await findUser(params.id)
- if (!user) {
- setResponseStatus(404)
- return new Response('User not found')
- }
- return json(user)
- },
- })
- ```
-
-In this example, we're returning a `404` status code if the user is not found. You can set any valid HTTP status code using this method.
-
-## Setting headers in the response
-
-Sometimes you may need to set headers in the response. You can do this by either:
-
-- Passing an object as the second argument to the `Response` constructor.
-
- ```ts
- // routes/api/hello.ts
- import { createAPIFileRoute } from '@tanstack/react-start/api'
-
- export const APIRoute = createAPIFileRoute('/api/hello')({
- GET: async ({ request }) => {
- return new Response('Hello, World!', {
- headers: {
- 'Content-Type': 'text/plain',
- },
- })
- },
- })
-
- // Visit /api/hello to see the response
- // Hello, World!
- ```
-
-- Or using the `setHeaders` helper function from `@tanstack/react-start/server`.
-
- ```ts
- // routes/api/hello.ts
- import { createAPIFileRoute } from '@tanstack/react-start/api'
- import { setHeaders } from '@tanstack/react-start/server'
-
- export const APIRoute = createAPIFileRoute('/api/hello')({
- GET: async ({ request }) => {
- setHeaders({
- 'Content-Type': 'text/plain',
- })
- return new Response('Hello, World!')
- },
- })
- ```
diff --git a/docs/start/framework/react/build-from-scratch.md b/docs/start/framework/react/build-from-scratch.md
index 5cb2e8e483..114ade8273 100644
--- a/docs/start/framework/react/build-from-scratch.md
+++ b/docs/start/framework/react/build-from-scratch.md
@@ -47,14 +47,12 @@ We highly recommend using TypeScript with TanStack Start. Create a `tsconfig.jso
## Install Dependencies
-TanStack Start is (currently\*) powered by [Vinxi](https://vinxi.vercel.app/) and [TanStack Router](https://tanstack.com/router) and requires them as dependencies.
-
-> [!NOTE] > \*Vinxi will be removed before version 1.0.0 is released and TanStack will rely only on Vite and Nitro. The commands and APIs that use Vinxi will likely be replaced with a Vite plugin or dedicated TanStack Start CLI.
+TanStack Start is (currently\*) powered by [Vite](https://vite.dev/) and [TanStack Router](https://tanstack.com/router) and requires them as dependencies.
To install them, run:
```shell
-npm i @tanstack/react-start @tanstack/react-router vinxi
+npm i @tanstack/react-start @tanstack/react-router vite
```
You'll also need React and the Vite React plugin, so install them too:
@@ -72,16 +70,16 @@ npm i -D typescript @types/react @types/react-dom
## Update Configuration Files
-We'll then update our `package.json` to use Vinxi's CLI and set `"type": "module"`:
+We'll then update our `package.json` to set `"type": "module"`:
```json
{
// ...
"type": "module",
"scripts": {
- "dev": "vinxi dev",
- "build": "vinxi build",
- "start": "vinxi start"
+ "dev": "vite dev",
+ "build": "vite build",
+ "start": "vite start"
}
}
```
@@ -106,12 +104,10 @@ export default defineConfig({
## Add the Basic Templating
-There are four required files for TanStack Start usage:
+There are 2 required files for TanStack Start usage:
1. The router configuration
-2. The server entry point
-3. The client entry point
-4. The root of your application
+2. The root of your application
Once configuration is done, we'll have a file tree that looks like the following:
@@ -120,10 +116,8 @@ Once configuration is done, we'll have a file tree that looks like the following
├── app/
│ ├── routes/
│ │ └── `__root.tsx`
-│ ├── `client.tsx`
│ ├── `router.tsx`
│ ├── `routeTree.gen.ts`
-│ └── `ssr.tsx`
├── `.gitignore`
├── `app.config.ts`
├── `package.json`
@@ -159,48 +153,6 @@ declare module '@tanstack/react-router' {
}
```
-## The Server Entry Point
-
-As TanStack Start is an [SSR](https://unicorn-utterances.com/posts/what-is-ssr-and-ssg) framework, we need to pipe this router
-information to our server entry point:
-
-```tsx
-// app/ssr.tsx
-import {
- createStartHandler,
- defaultStreamHandler,
-} from '@tanstack/react-start/server'
-import { getRouterManifest } from '@tanstack/react-start/router-manifest'
-
-import { createRouter } from './router'
-
-export default createStartHandler({
- createRouter,
- getRouterManifest,
-})(defaultStreamHandler)
-```
-
-This allows us to know what routes and loaders we need to execute when the user hits a given route.
-
-## The Client Entry Point
-
-Now we need a way to hydrate our client-side JavaScript once the route resolves to the client. We do this by piping the same
-router information to our client entry point:
-
-```tsx
-// app/client.tsx
-///
-import { hydrateRoot } from 'react-dom/client'
-import { StartClient } from '@tanstack/react-start'
-import { createRouter } from './router'
-
-const router = createRouter()
-
-hydrateRoot(document, )
-```
-
-This enables us to kick off client-side routing once the user's initial server request has fulfilled.
-
## The Root of Your Application
Finally, we need to create the root of our application. This is the entry point for all other routes. The code in this file will wrap all other routes in the application.
diff --git a/docs/start/framework/react/hosting.md b/docs/start/framework/react/hosting.md
index c7ef0d3485..d5dfeff318 100644
--- a/docs/start/framework/react/hosting.md
+++ b/docs/start/framework/react/hosting.md
@@ -3,7 +3,7 @@ id: hosting
title: Hosting
---
-Hosting is the process of deploying your application to the internet so that users can access it. This is a critical part of any web development project, ensuring your application is available to the world. TanStack Start is built on [Nitro](https://nitro.unjs.io/), a powerful server toolkit for deploying web applications anywhere. Nitro allows TanStack Start to provide a unified API for SSR, streaming, and hydration on any hosting provider.
+Hosting is the process of deploying your application to the internet so that users can access it. This is a critical part of any web development project, ensuring your application is available to the world. TanStack Start is built on Vite, a powerful dev/build platform that allows us to make it possible to deploy your application to any hosting provider.
## What should I use?
diff --git a/docs/start/framework/react/learn-the-basics.md b/docs/start/framework/react/learn-the-basics.md
index 4703259c5a..fa8a6451be 100644
--- a/docs/start/framework/react/learn-the-basics.md
+++ b/docs/start/framework/react/learn-the-basics.md
@@ -7,13 +7,10 @@ This guide will help you learn the basics behind how TanStack Start works, regar
## Dependencies
-TanStack Start is (currently\*) powered by [Vinxi](https://vinxi.vercel.app/), [Nitro](https://nitro.unjs.io/) and [TanStack Router](https://tanstack.com/router).
+TanStack Start is powered by [Vite](https://vite.dev/) and [TanStack Router](https://tanstack.com/router).
- **TanStack Router**: A router for building web applications.
-- **Nitro**: A framework for building server applications.
-- **Vinxi**: A server framework for building web applications.
-
-> [!NOTE] Vinxi will be removed before version 1.0.0 is released and TanStack will rely only on Vite and Nitro. The commands and APIs that use Vinxi will likely be replaced with a Vite plugin.
+- **Vite**: A build tool for bundling your application.
## It all "Starts" with the Router
@@ -47,14 +44,15 @@ declare module '@tanstack/react-router' {
The `routeTree.gen.ts` file is generated when you run TanStack Start (via `npm run dev` or `npm run start`) for the first time. This file contains the generated route tree and a handful of TS utilities that make TanStack Start fully type-safe.
-## The Server Entry Point
+## The Server Entry Point (Optional)
-Although TanStack Start is designed with client-first APIs, it is by and large, a full-stack framework. This means that all use cases, including both dynamic and static rely on a server or build-time entry to render our application's initial HTML payload.
+> [!NOTE]
+> The server entry point is **optional** out of the box. If not provided, TanStack Start will automatically handle the server entry point for you using the below as a default.
-This is done via the `app/ssr.tsx` file:
+This is done via the `src/server.tsx` file:
```tsx
-// app/ssr.tsx
+// src/server.tsx
import {
createStartHandler,
defaultStreamHandler,
@@ -69,13 +67,16 @@ export default createStartHandler({
})(defaultStreamHandler)
```
-Whether we are statically generating our app or serving it dynamically, the `ssr.tsx` file is the entry point for doing all SSR-related work.
+Whether we are statically generating our app or serving it dynamically, the `server.tsx` file is the entry point for doing all SSR-related work.
- It's important that a new router is created for each request. This ensures that any data handled by the router is unique to the request.
- The `getRouterManifest` function is used to generate the router manifest, which is used to determine many aspects of asset management and preloading for our application.
- The `defaultStreamHandler` function is used to render our application to a stream, allowing us to take advantage of streaming HTML to the client. (This is the default handler, but you can also use other handlers like `defaultRenderHandler`, or even build your own)
-## The Client Entry Point
+## The Client Entry Point (Optional)
+
+> [!NOTE]
+> The client entry point is **optional** out of the box. If not provided, TanStack Start will automatically handle the client entry point for you using the below as a default.
Getting our html to the client is only half the battle. Once there, we need to hydrate our client-side JavaScript once the route resolves to the client. We do this by hydrating the root of our application with the `StartClient` component:
@@ -94,7 +95,7 @@ This enables us to kick off client-side routing once the user's initial server r
## The Root of Your Application
-Other than the client entry point, the `__root` route of your application is the entry point for your application. The code in this file will wrap all other routes in the app, including your home page. It behaves like a pathless layout route for your whole application.
+Other than the client entry point (which is optional by default), the `__root` route of your application is the entry point for your application. The code in this file will wrap all other routes in the app, including your home page. It behaves like a pathless layout route for your whole application.
Because it is **always rendered**, it is the perfect place to construct your application shell and take care of any global logic.
diff --git a/docs/start/framework/react/overview.md b/docs/start/framework/react/overview.md
index 84e036c7b3..b656127b06 100644
--- a/docs/start/framework/react/overview.md
+++ b/docs/start/framework/react/overview.md
@@ -3,7 +3,7 @@ id: overview
title: TanStack Start Overview
---
-TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like [Nitro](https://nitro.unjs.io/) and [Vite](https://vitejs.dev/). It is ready to deploy to your favorite hosting provider!
+TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more. Thanks to [Vite](https://vite.dev/), it's ready to develop and deploy to any hosting provider or runtime you want!
## Router or Start?
@@ -39,7 +39,7 @@ What you get with TanStack Start:
## How does it work?
-TanStack Start uses [Nitro](https://nitro.unjs.io/) and [Vite](https://vitejs.dev/) to bundle and deploy your application. In fact, these are the same tools that power Solid Start! With these tools, we can do a few things we couldn't do before:
+TanStack Start uses [Vite](https://vitejs.dev/) to bundle and deploy your application and empowers amazing features like:
- Provide a unified API for SSR, streaming, and hydration
- Extract server-only code from your client-side code (e.g. server functions)
diff --git a/docs/start/framework/react/server-routes.md b/docs/start/framework/react/server-routes.md
new file mode 100644
index 0000000000..44ba76293b
--- /dev/null
+++ b/docs/start/framework/react/server-routes.md
@@ -0,0 +1,387 @@
+---
+id: server-routes
+title: Server Routes
+---
+
+// TODO: Add redirect from api-routes to server-routes
+
+Server routes are a powerful feature of TanStack Start that allow you to create server-side endpoints in your application and are useful for handling raw HTTP requests, form submissions, user authentication, and much more.
+
+Server routes can be defined in your `./app/routes` directory of your project **right alongside your TanStack Router routes** and are automatically handled by the TanStack Start server.
+
+Here's what a simple server route looks like:
+
+```ts
+// routes/hello.ts
+
+export const ServerRoute = createServerRoute({
+ GET: async ({ request }) => {
+ return new Response('Hello, World!')
+ },
+})
+```
+
+## Server Routes and App Routes
+
+Because server routes can be defined in the same directory as your app routes, you can even use the same file for both!
+
+```tsx
+// routes/hello.ts
+
+export const ServerRoute = createServerRoute().methods({
+ POST: async ({ request }) => {
+ const body = await request.json()
+ return new Response(JSON.stringify({ message: `Hello, ${body.name}!` }))
+ },
+})
+
+export const Route = createFileRoute({
+ component: HelloComponent,
+})
+
+function HelloComponent() {
+ const [reply, setReply] = useState('')
+
+ return (
+
+
+
+ )
+}
+```
+
+## File Route Conventions
+
+Server routes in TanStack Start, follow the same file-based routing conventions as TanStack Router. This means that each file in your `routes` directory with a `ServerRoute` export will be treated as an API route. Here are a few examples:
+
+- `/routes/users.ts` will create an API route at `/users`
+- `/routes/users.index.ts` will **also** create an API route at `/users` (but will error if duplicate methods are defined)
+- `/routes/users/$id.ts` will create an API route at `/users/$id`
+- `/routes/users/$id/posts.ts` will create an API route at `/users/$id/posts`
+- `/routes/users.$id.posts.ts` will create an API route at `/users/$id/posts`
+- `/routes/file/$.ts` will create an API route at `/api/file/$`
+- `/routes/my-script[.]js.ts` will create an API route at `/my-script.js`
+
+## Unique Route Paths
+
+Each route can only have a single handler file associated with it. So, if you have a file named `routes/users.ts` which'd equal the request path of `/api/users`, you cannot have other files that'd also resolve to the same route. For example, the following files would all resolve to the same route and would error:
+
+- `/routes/users.index.ts`
+- `/routes/users.ts`
+- `/routes/users.index.ts`
+
+## Escaped Matching
+
+Just as with normal routes, server routes can match on escaped characters. For example, a file named `routes/users[.]ts` will create an API route at `/api/users[.]`.
+
+## Pathless Layout Routes and Break-out Routes
+
+Because of the unified routing system, pathless layout routes and break-out routes are supported for similar functionality around server route middleware.
+
+- Pathless layout routes can be used to add middleware to a group of routes
+- Break-out routes can be used to "break out" of parent middleware
+
+## Nested Directories vs File-names
+
+In the examples above, you may have noticed that the file naming conventions are flexible and allow you to mix and match directories and file names. This is intentional and allows you to organize your Server routes in a way that makes sense for your application. You can read more about this in the [TanStack Router File-based Routing Guide](/router/latest/docs/framework/react/routing/file-based-routing#s-or-s).
+
+## Handling Server Route Requests
+
+Server route requests are handled by Start's `createStartHandler` in your `server.ts` entry file.
+
+```tsx
+// server.ts
+import { createStartHandler } from '@tanstack/react-start/server'
+
+export default createStartHandler({
+ createRouter,
+ getRouterManifest,
+})(defaultStreamHandler)
+```
+
+The start handler is responsible for matching an incoming request to a server route and executing the appropriate middleware, validators and handler.
+
+Remember, if you need to customize the server handler, you can do so by creating a custom handler and then passing the event to the start handler:
+
+```tsx
+// server.ts
+import { createStartHandler } from '@tanstack/react-start/server'
+
+export default defineHandler((event) => {
+ const startHandler = createStartHandler({
+ createRouter,
+ getRouterManifest,
+ })(defaultStreamHandler)
+
+ return startHandler(event)
+})
+```
+
+## Defining an Server Route
+
+Server routes are created by exporting a `ServerRoute` from a route file. The `ServerRoute` export should be created by calling the `createServerFileRoute` function. The resulting builder object can then be used to:
+
+- Add route-level middleware
+- Define handlers for each HTTP method
+- Add middleware and validators to specific HTTP methods
+
+```ts
+// routes/hello.ts
+export const ServerRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ return new Response('Hello, World! from ' + request.url)
+ },
+})
+```
+
+## Defining a Server Route Handler
+
+There are two ways to define a handler for a server route.
+
+- Provide a handler function directly to the method
+- By calling the `handler` method on the method builder object for more advanced use cases
+
+### Providing a handler function directly to the method
+
+For simple use cases, you can provide a handler function directly to the method.
+
+```ts
+// routes/hello.ts
+export const ServerRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ return new Response('Hello, World! from ' + request.url)
+ },
+})
+```
+
+### Providing a handler function via the method builder object
+
+For more complex use cases, you can provide a handler function via the method builder object. This allows you to:
+
+- Add middleware to the method
+- Add a validator to the method
+
+```tsx
+// routes/hello.ts
+export const ServerRoute = createServerFileRoute().methods((api) => ({
+ GET: api
+ .middleware([loggerMiddleware])
+ .validator(z.object({ name: z.string() }))
+ .handler(async ({ request }) => {
+ return new Response('Hello, World! from ' + request.url)
+ }),
+}))
+```
+
+## Handler Context
+
+Each HTTP method handler receives an object with the following properties:
+
+- `request`: The incoming request object. You can read more about the `Request` object in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Request).
+- `params`: An object containing the dynamic path parameters of the route. For example, if the route path is `/users/$id`, and the request is made to `/users/123`, then `params` will be `{ id: '123' }`. We'll cover dynamic path parameters and wildcard parameters later in this guide.
+- `context`: An object containing the context of the request. This is useful for passing data between middleware.
+
+Once you've processed the request, you can return a `Response` object or `Promise` or even use any of the helpers from `@tanstack/react-start` to manipulate the response.
+
+## Dynamic Path Params
+
+Server routes support dynamic path parameters in the same way as TanStack Router. For example, a file named `routes/users/$id.ts` will create an API route at `/users/$id` that accepts a dynamic `id` parameter.
+
+```ts
+// routes/users/$id.ts
+export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ params }) => {
+ const { id } = params
+ return new Response(`User ID: ${id}`)
+ },
+})
+
+// Visit /api/users/123 to see the response
+// User ID: 123
+```
+
+You can also have multiple dynamic path parameters in a single route. For example, a file named `routes/users/$id/posts/$postId.ts` will create an API route at `/api/users/$id/posts/$postId` that accepts two dynamic parameters.
+
+```ts
+// routes/users/$id/posts/$postId.ts
+export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ params }) => {
+ const { id, postId } = params
+ return new Response(`User ID: ${id}, Post ID: ${postId}`)
+ },
+})
+
+// Visit /api/users/123/posts/456 to see the response
+// User ID: 123, Post ID: 456
+```
+
+## Wildcard/Splat Param
+
+Server routes also support wildcard parameters at the end of the path, which are denoted by a `$` followed by nothing. For example, a file named `routes/file/$.ts` will create an API route at `/api/file/$` that accepts a wildcard parameter.
+
+```ts
+// routes/file/$.ts
+export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ params }) => {
+ const { _splat } = params
+ return new Response(`File: ${_splat}`)
+ },
+})
+
+// Visit /api/file/hello.txt to see the response
+// File: hello.txt
+```
+
+## Handling requests with a body
+
+To handle POST requests,you can add a `POST` handler to the route object. The handler will receive the request object as the first argument, and you can access the request body using the `request.json()` method.
+
+```ts
+// routes/hello.ts
+export const APIRoute = createServerFileRoute().methods({
+ POST: async ({ request }) => {
+ const body = await request.json()
+ return new Response(`Hello, ${body.name}!`)
+ },
+})
+
+// Send a POST request to /api/hello with a JSON body like { "name": "Tanner" }
+// Hello, Tanner!
+```
+
+This also applies to other HTTP methods like `PUT`, `PATCH`, and `DELETE`. You can add handlers for these methods in the route object and access the request body using the appropriate method.
+
+It's important to remember that the `request.json()` method returns a `Promise` that resolves to the parsed JSON body of the request. You need to `await` the result to access the body.
+
+This is a common pattern for handling POST requests in Server routes/ You can also use other methods like `request.text()` or `request.formData()` to access the body of the request.
+
+## Responding with JSON
+
+When returning JSON using a Response object, this is a common pattern:
+
+```ts
+// routes/hello.ts
+export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ return new Response(JSON.stringify({ message: 'Hello, World!' }), {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ },
+})
+
+// Visit /api/hello to see the response
+// {"message":"Hello, World!"}
+```
+
+## Using the `json` helper function
+
+Or you can use the `json` helper function to automatically set the `Content-Type` header to `application/json` and serialize the JSON object for you.
+
+```ts
+// routes/hello.ts
+import { json } from '@tanstack/react-start'
+
+export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ return json({ message: 'Hello, World!' })
+ },
+})
+
+// Visit /api/hello to see the response
+// {"message":"Hello, World!"}
+```
+
+## Responding with a status code
+
+You can set the status code of the response by either:
+
+- Passing it as a property of the second argument to the `Response` constructor
+
+ ```ts
+ // routes/hello.ts
+ import { json } from '@tanstack/react-start'
+
+ export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request, params }) => {
+ const user = await findUser(params.id)
+ if (!user) {
+ return new Response('User not found', {
+ status: 404,
+ })
+ }
+ return json(user)
+ },
+ })
+ ```
+
+- Using the `setResponseStatus` helper function from `@tanstack/react-start/server`
+
+ ```ts
+ // routes/hello.ts
+ import { json } from '@tanstack/react-start'
+ import { setResponseStatus } from '@tanstack/react-start/server'
+
+ export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request, params }) => {
+ const user = await findUser(params.id)
+ if (!user) {
+ setResponseStatus(404)
+ return new Response('User not found')
+ }
+ return json(user)
+ },
+ })
+ ```
+
+In this example, we're returning a `404` status code if the user is not found. You can set any valid HTTP status code using this method.
+
+## Setting headers in the response
+
+Sometimes you may need to set headers in the response. You can do this by either:
+
+- Passing an object as the second argument to the `Response` constructor.
+
+ ```ts
+ // routes/hello.ts
+ export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ return new Response('Hello, World!', {
+ headers: {
+ 'Content-Type': 'text/plain',
+ },
+ })
+ },
+ })
+
+ // Visit /api/hello to see the response
+ // Hello, World!
+ ```
+
+- Or using the `setHeaders` helper function from `@tanstack/react-start/server`.
+
+ ```ts
+ // routes/hello.ts
+ import { setHeaders } from '@tanstack/react-start/server'
+
+ export const APIRoute = createServerFileRoute().methods({
+ GET: async ({ request }) => {
+ setHeaders({
+ 'Content-Type': 'text/plain',
+ })
+ return new Response('Hello, World!')
+ },
+ })
+ ```
diff --git a/docs/start/framework/react/ssr.md b/docs/start/framework/react/ssr.md
index f509d47f72..b932548555 100644
--- a/docs/start/framework/react/ssr.md
+++ b/docs/start/framework/react/ssr.md
@@ -7,10 +7,10 @@ Server-side rendering (SSR) is the process of rendering your application on the
## SSR Basics
-TanStack Start supports server-side rendering out of the box. To enable server-side rendering, create an `app/ssr.tsx` file in your project:
+TanStack Start supports server-side rendering out of the box. To enable server-side rendering, create an `src/server.tsx` file in your project:
```tsx
-// app/ssr.tsx
+// src/server.tsx
import {
createStartHandler,
diff --git a/docs/start/framework/react/static-prerendering.md b/docs/start/framework/react/static-prerendering.md
index 768e11d539..30111f24fb 100644
--- a/docs/start/framework/react/static-prerendering.md
+++ b/docs/start/framework/react/static-prerendering.md
@@ -3,13 +3,11 @@ id: static-prerendering
title: Static Prerendering
---
-> Static Prerendering is a feature of Nitro, and while it is available in TanStack Start, we are still exploring the best practices for using it. Tread lightly!
-
Static prerendering is the process of generating static HTML files for your application. This can be useful for either improving the performance of your application, as it allows you to serve pre-rendered HTML files to users without having to generate them on the fly or for deploying static sites to platforms that do not support server-side rendering.
-## Prerendering, powered by Nitro
+## Prerendering
-TanStack Start is built on Nitro, which means we can take advantage of Nitro's prerendering capabilities. Nitro can prerender your application to static HTML files, which can then be served to users without having to generate them on the fly. To prerender your application, you can add the `server.prerender` option to your `app.config.js` file:
+TanStack Start can prerender your application to static HTML files, which can then be served to users without having to generate them on the fly. To prerender your application, you can add the `server.prerender` option to your `app.config.js` file:
```js
// app.config.js
@@ -25,39 +23,3 @@ export default defineConfig({
},
})
```
-
-Many of the options available for prerendering are documented in the [Nitro config prerender documentation](https://nitro.unjs.io/config#prerender).
-
-## Prerendering dynamic routes with Nitro
-
-Nitro ships with some prebuilt hooks that let you customize the prerendering process among other things. One of these hooks is the `prerender:routes` hook. This hook allows you to fetch async data and add routes to a `Set` of routes to be prerendered.
-
-For this example, let's pretend we have a blog with a list of posts. We want to prerender each post page. Our post route looks like `/posts/$postId`. We can use the `prerender:routes` hook to fetch the all of our posts and add each post path to the routes set.
-
-```ts
-// app.config.ts
-import { defineConfig } from '@tanstack/react-start/config'
-
-export default defineConfig({
- server: {
- hooks: {
- 'prerender:routes': async (routes) => {
- // fetch the pages you want to render
- const posts = await fetch('https://api.example.com/posts')
- const postsData = await posts.json()
-
- // add each post path to the routes set
- postsData.forEach((post) => {
- routes.add(`/posts/${post.id}`)
- })
- },
- },
- prerender: {
- routes: ['/'],
- crawlLinks: true,
- },
- },
-})
-```
-
-As of writing, the [Nitro hooks documentation](https://nitro.build/config#hooks) does not include any information on the provided hooks.
diff --git a/e2e/create-start/utils/setup.ts b/e2e/create-start/utils/setup.ts
index bfb4c1fd53..5f0af79fee 100644
--- a/e2e/create-start/utils/setup.ts
+++ b/e2e/create-start/utils/setup.ts
@@ -15,7 +15,7 @@ async function _setup(
const ADDR = `http://localhost:${port}`
const childProcess = exec(
- `VITE_SERVER_PORT=${port} pnpm vinxi dev --port ${port}`,
+ `VITE_SERVER_PORT=${port} pnpm vite dev --port ${port}`,
{
cwd: projectPath,
},
diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts
index 0cc7397492..e15a1921e5 100644
--- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts
+++ b/e2e/react-router/basic-file-based/src/routeTree.gen.ts
@@ -432,6 +432,152 @@ declare module '@tanstack/react-router' {
}
}
+declare module './routes/index' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/'>
+ >
+}
+declare module './routes/_layout' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/_layout'>
+ >
+}
+declare module './routes/anchor' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/anchor'>
+ >
+}
+declare module './routes/editing-a' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/editing-a'>
+ >
+}
+declare module './routes/editing-b' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/editing-b'>
+ >
+}
+declare module './routes/posts' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/posts'>
+ >
+}
+declare module './routes/(another-group)/onlyrouteinside' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(another-group)/onlyrouteinside'>
+ >
+}
+declare module './routes/(group)' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)'>
+ >
+}
+declare module './routes/(group)/_layout' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)/_layout'>
+ >
+}
+declare module './routes/(group)/inside' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)/inside'>
+ >
+}
+declare module './routes/(group)/lazyinside' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)/lazyinside'>
+ >
+}
+declare module './routes/_layout/_layout-2' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/_layout/_layout-2'>
+ >
+}
+declare module './routes/posts.$postId' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/posts/$postId'>
+ >
+}
+declare module './routes/redirect/$target' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/$target'>
+ >
+}
+declare module './routes/structural-sharing.$enabled' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/structural-sharing/$enabled'>
+ >
+}
+declare module './routes/posts.index' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/posts/'>
+ >
+}
+declare module './routes/redirect/index' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/'>
+ >
+}
+declare module './routes/(group)/_layout.insidelayout' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)/_layout/insidelayout'>
+ >
+}
+declare module './routes/(group)/subfolder/inside' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/(group)/subfolder/inside'>
+ >
+}
+declare module './routes/_layout/_layout-2/layout-a' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/_layout/_layout-2/layout-a'>
+ >
+}
+declare module './routes/_layout/_layout-2/layout-b' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/_layout/_layout-2/layout-b'>
+ >
+}
+declare module './routes/params.single.$value' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/params/single/$value'>
+ >
+}
+declare module './routes/posts_.$postId.edit' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/posts_/$postId/edit'>
+ >
+}
+declare module './routes/redirect/$target/via-beforeLoad' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/$target/via-beforeLoad'>
+ >
+}
+declare module './routes/redirect/$target/via-loader' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/$target/via-loader'>
+ >
+}
+declare module './routes/redirect/preload/first' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/preload/first'>
+ >
+}
+declare module './routes/redirect/preload/second' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/preload/second'>
+ >
+}
+declare module './routes/redirect/preload/third' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/preload/third'>
+ >
+}
+declare module './routes/redirect/$target/index' {
+ const createFileRoute: ReturnType<
+ typeof import('@tanstack/react-router').createFileRouteImpl<'/redirect/$target/'>
+ >
+}
+
// Create and export the route tree
interface LayoutLayout2RouteChildren {
diff --git a/e2e/react-router/basic-file-based/src/routes/(another-group)/onlyrouteinside.tsx b/e2e/react-router/basic-file-based/src/routes/(another-group)/onlyrouteinside.tsx
index bbd1eedb6f..bcc87859bb 100644
--- a/e2e/react-router/basic-file-based/src/routes/(another-group)/onlyrouteinside.tsx
+++ b/e2e/react-router/basic-file-based/src/routes/(another-group)/onlyrouteinside.tsx
@@ -4,7 +4,7 @@ import { zodValidator } from '@tanstack/zod-adapter'
const routeApi = getRouteApi('/(another-group)/onlyrouteinside')
-export const Route = createFileRoute('/(another-group)/onlyrouteinside')({
+export const Route = createFileRoute({
validateSearch: zodValidator(z.object({ hello: z.string().optional() })),
component: () => {
const searchViaHook = useSearch({
diff --git a/e2e/react-router/basic-file-based/src/routes/(group)/_layout.insidelayout.tsx b/e2e/react-router/basic-file-based/src/routes/(group)/_layout.insidelayout.tsx
index 2975baedc9..4e1ddc648c 100644
--- a/e2e/react-router/basic-file-based/src/routes/(group)/_layout.insidelayout.tsx
+++ b/e2e/react-router/basic-file-based/src/routes/(group)/_layout.insidelayout.tsx
@@ -4,7 +4,7 @@ import { zodValidator } from '@tanstack/zod-adapter'
const routeApi = getRouteApi('/(group)/_layout/insidelayout')
-export const Route = createFileRoute('/(group)/_layout/insidelayout')({
+export const Route = createFileRoute({
validateSearch: zodValidator(z.object({ hello: z.string().optional() })),
component: () => {
const searchViaHook = useSearch({ from: '/(group)/_layout/insidelayout' })
diff --git a/e2e/react-router/basic-file-based/src/routes/(group)/_layout.tsx b/e2e/react-router/basic-file-based/src/routes/(group)/_layout.tsx
index 4b755791c0..31f57f0fb5 100644
--- a/e2e/react-router/basic-file-based/src/routes/(group)/_layout.tsx
+++ b/e2e/react-router/basic-file-based/src/routes/(group)/_layout.tsx
@@ -1,6 +1,6 @@
import { Outlet, createFileRoute } from '@tanstack/react-router'
-export const Route = createFileRoute('/(group)/_layout')({
+export const Route = createFileRoute({
component: () => (
<>