Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions docs/router/framework/solid/installation/with-esbuild.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Installation with Esbuild
---

[//]: # 'BundlerConfiguration'

To use file-based routing with **Esbuild**, you'll need to install the `@tanstack/router-plugin` package.

```sh
npm install -D @tanstack/router-plugin
```

Once installed, you'll need to add the plugin to your configuration.

```tsx
// build.js
import * as esbuild from 'esbuild'
import { solidPlugin } from 'esbuild-plugin-solid'
import { tanstackRouter } from '@tanstack/router-plugin/esbuild'

const isDev = process.argv.includes('--dev')

const ctx = await esbuild.context({
entryPoints: ['src/main.tsx'],
outfile: 'dist/main.js',
minify: !isDev,
bundle: true,
format: 'esm',
target: ['esnext'],
sourcemap: true,
plugins: [
solidPlugin(),
tanstackRouter({ target: 'solid', autoCodeSplitting: true }),
],
})

if (isDev) {
await ctx.watch()
const { host, port } = await ctx.serve({ servedir: '.', port: 3005 })
console.log(`Server running at http://${host || 'localhost'}:${port}`)
} else {
await ctx.rebuild()
await ctx.dispose()
}
```

Or, you can clone our [Quickstart Esbuild example](https://github.com/TanStack/router/tree/main/examples/solid/quickstart-esbuild-file-based) and get started.

Now that you've added the plugin to your Esbuild configuration, you're all set to start using file-based routing with TanStack Router.

[//]: # 'BundlerConfiguration'

## Ignoring the generated route tree file

If your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter.

Here are some resources to help you ignore the generated route tree file:

- Prettier - [https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore](https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore)
- ESLint - [https://eslint.org/docs/latest/use/configure/ignore#ignoring-files](https://eslint.org/docs/latest/use/configure/ignore#ignoring-files)
- Biome - [https://biomejs.dev/reference/configuration/#filesignore](https://biomejs.dev/reference/configuration/#filesignore)

> [!WARNING]
> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route.

You can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings:

```json
{
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
}
}
```

You can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project.

## Configuration

When using the TanStack Router Plugin with Esbuild for File-based routing, it comes with some sane defaults that should work for most projects:

```json
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts",
"routeFileIgnorePrefix": "-",
"quoteStyle": "single"
}
```

If these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function.

You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md).
5 changes: 5 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
11 changes: 11 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
}
}
6 changes: 6 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install` or `yarn`
- `npm start` or `yarn start`
29 changes: 29 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node
import * as esbuild from 'esbuild'
import { solidPlugin } from 'esbuild-plugin-solid'
import { tanstackRouter } from '@tanstack/router-plugin/esbuild'

const isDev = process.argv.includes('--dev')

const ctx = await esbuild.context({
entryPoints: ['src/main.tsx'],
outfile: 'dist/main.js',
minify: !isDev,
bundle: true,
format: 'esm',
target: ['esnext'],
sourcemap: true,
plugins: [
solidPlugin(),
tanstackRouter({ target: 'solid', autoCodeSplitting: true }),
],
})

if (isDev) {
await ctx.watch()
const { host, port } = await ctx.serve({ servedir: '.', port: 3005 })
console.log(`Server running at http://${host || 'localhost'}:${port}`)
} else {
await ctx.rebuild()
await ctx.dispose()
}
24 changes: 24 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update the page title to reflect esbuild setup.

The title currently says "Vite App" but this is an esbuild-based example, not Vite.

Apply this diff:

-    <title>Vite App</title>
+    <title>TanStack Router + Solid + Esbuild</title>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<title>Vite App</title>
<title>TanStack Router + Solid + Esbuild</title>
🤖 Prompt for AI Agents
In examples/solid/quickstart-esbuild-file-based/index.html at line 6, the page
title incorrectly reads "Vite App"; update the <title> element text to reflect
this is an esbuild example (e.g., "esbuild Quickstart" or "Solid + esbuild
Quickstart") so the page title accurately describes the project.

<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
html {
color-scheme: light dark;
}
* {
@apply border-gray-200 dark:border-gray-800;
}
body {
@apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/dist/main.js"></script>
</body>
</html>
23 changes: 23 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "tanstack-router-solid-example-quickstart-esbuild-file-based",
"private": true,
"type": "module",
"scripts": {
"dev": "node build.js --dev",
"build": "node build.js",
"serve": "node build.js --dev",
"start": "dev"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the start script reference.

The start script references "dev" as a bare string, which won't work correctly. It should reference the dev script properly.

Apply this diff:

   "scripts": {
     "dev": "node build.js --dev",
     "build": "node build.js",
     "serve": "node build.js --dev",
-    "start": "dev"
+    "start": "npm run dev"
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"start": "dev"
"scripts": {
"dev": "node build.js --dev",
"build": "node build.js",
"serve": "node build.js --dev",
"start": "npm run dev"
},
🤖 Prompt for AI Agents
In examples/solid/quickstart-esbuild-file-based/package.json around line 9, the
"start" script incorrectly contains the bare string "dev"; replace it with a
proper script reference such as "npm run dev" (or "pnpm run dev"/"yarn dev" to
match your package manager) so the start script invokes the dev script
correctly.

},
"dependencies": {
"@tanstack/solid-router": "^1.135.2",
"@tanstack/solid-router-devtools": "^1.135.2",
"@tanstack/router-plugin": "^1.135.2",
"solid-js": "^1.9.10",
"redaxios": "^0.5.1",
"zod": "^3.24.2"
},
Comment on lines +11 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use workspace protocol for internal TanStack dependencies.

According to the coding guidelines, internal dependencies should use the workspace:* protocol instead of version ranges. This ensures the example always uses the current workspace versions during development.

Apply this diff:

   "dependencies": {
-    "@tanstack/solid-router": "^1.135.2",
-    "@tanstack/solid-router-devtools": "^1.135.2",
-    "@tanstack/router-plugin": "^1.135.2",
+    "@tanstack/solid-router": "workspace:*",
+    "@tanstack/solid-router-devtools": "workspace:*",
+    "@tanstack/router-plugin": "workspace:*",
     "solid-js": "^1.9.10",
     "redaxios": "^0.5.1",
     "zod": "^3.24.2"
   },

Based on coding guidelines

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dependencies": {
"@tanstack/solid-router": "^1.135.2",
"@tanstack/solid-router-devtools": "^1.135.2",
"@tanstack/router-plugin": "^1.135.2",
"solid-js": "^1.9.10",
"redaxios": "^0.5.1",
"zod": "^3.24.2"
},
"dependencies": {
"@tanstack/solid-router": "workspace:*",
"@tanstack/solid-router-devtools": "workspace:*",
"@tanstack/router-plugin": "workspace:*",
"solid-js": "^1.9.10",
"redaxios": "^0.5.1",
"zod": "^3.24.2"
},
🤖 Prompt for AI Agents
In examples/solid/quickstart-esbuild-file-based/package.json around lines 11 to
18, the internal TanStack packages use fixed version ranges; change those to use
the workspace protocol. Replace the versions for "@tanstack/solid-router",
"@tanstack/solid-router-devtools", and "@tanstack/router-plugin" with
"workspace:*" so the example consumes local workspace packages during
development, leaving third-party deps (solid-js, redaxios, zod) unchanged.

"devDependencies": {
"esbuild": "^0.25.0",
"esbuild-plugin-solid": "^0.6.0"
}
}
23 changes: 23 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { render } from 'solid-js/web'
import { RouterProvider, createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'

// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
})

// Register things for typesafety
declare module '@tanstack/solid-router' {
interface Register {
router: typeof router
}
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
render(() => <RouterProvider router={router} />, rootElement)
}
33 changes: 33 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/src/posts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'redaxios'

export type PostType = {
id: string
title: string
body: string
}

export class PostNotFoundError extends Error {}

export const fetchPost = async (postId: string) => {
console.info(`Fetching post with id ${postId}...`)
await new Promise((r) => setTimeout(r, 500))
const post = await axios
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
.then((r) => r.data)
.catch((err) => {
if (err.status === 404) {
throw new PostNotFoundError(`Post with id "${postId}" not found!`)
}
throw err
})

return post
}

export const fetchPosts = async () => {
console.info('Fetching posts...')
await new Promise((r) => setTimeout(r, 500))
return axios
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
.then((r) => r.data.slice(0, 10))
}
111 changes: 111 additions & 0 deletions examples/solid/quickstart-esbuild-file-based/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* prettier-ignore-start */

/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file is auto-generated by TanStack Router

// Import Routes

import { Route as rootRoute } from './routes/__root'
import { Route as AboutImport } from './routes/about'
import { Route as IndexImport } from './routes/index'

// Create/Update Routes

const AboutRoute = AboutImport.update({
path: '/about',
getParentRoute: () => rootRoute,
} as any)

const IndexRoute = IndexImport.update({
path: '/',
getParentRoute: () => rootRoute,
} as any)

// Populate the FileRoutesByPath interface

declare module '@tanstack/solid-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/about': {
id: '/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute
}
}
}

// Create and export the route tree

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}

export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}

export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
'/about': typeof AboutRoute
}

export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/about'
id: '__root__' | '/' | '/about'
fileRoutesById: FileRoutesById
}

export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
}

export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()

/* prettier-ignore-end */

/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/about"
]
},
"/": {
"filePath": "index.tsx"
},
"/about": {
"filePath": "about.tsx"
}
}
}
ROUTE_MANIFEST_END */
Loading
Loading