diff --git a/docs/guides/ecosystem/tanstack-start.mdx b/docs/guides/ecosystem/tanstack-start.mdx
index 27d6c01255..d7508b3b81 100644
--- a/docs/guides/ecosystem/tanstack-start.mdx
+++ b/docs/guides/ecosystem/tanstack-start.mdx
@@ -48,107 +48,711 @@ mode: center
## Hosting
-
-
- Add [Nitro](https://nitro.build/) to your project. This tool allows you to deploy your TanStack Start app to different platforms.
+To host your TanStack Start app, you can use [Nitro](https://nitro.build/) or a custom Bun server for production deployments.
- ```sh terminal icon="terminal"
- bun add nitro
- ```
+
+
+
+
+ Add [Nitro](https://nitro.build/) to your project. This tool allows you to deploy your TanStack Start app to different platforms.
-
- Update your vite.config.ts file}>
- Update your `vite.config.ts` file to include the necessary plugins for TanStack Start with Bun.
+ ```sh terminal icon="terminal"
+ bun add nitro
+ ```
- ```ts vite.config.ts icon="/icons/typescript.svg"
- // other imports...
- import { nitro } from "nitro/vite"; // [!code ++]
+
+ Update your vite.config.ts file}>
+ Update your `vite.config.ts` file to include the necessary plugins for TanStack Start with Bun.
- const config = defineConfig({
- plugins: [
- tanstackStart(),
- nitro({ preset: "bun" }), // [!code ++]
- // other plugins...
- ],
- });
+ ```ts vite.config.ts icon="/icons/typescript.svg"
+ // other imports...
+ import { nitro } from "nitro/vite"; // [!code ++]
- export default config;
- ```
+ const config = defineConfig({
+ plugins: [
+ tanstackStart(),
+ nitro({ preset: "bun" }), // [!code ++]
+ // other plugins...
+ ],
+ });
-
- The `bun` preset is optional, but it configures the build output specifically for Bun's runtime.
-
+ export default config;
+ ```
-
-
- Make sure `build` and `start` scripts are present in your `package.json` file:
+
+ The `bun` preset is optional, but it configures the build output specifically for Bun's runtime.
+
- ```json package.json icon="file-json"
- {
- "scripts": {
- "build": "bun --bun vite build", // [!code ++]
- // The .output files are created by Nitro when you run `bun run build`.
- // Not necessary when deploying to Vercel.
- "start": "bun run .output/server/index.mjs" // [!code ++]
- }
- }
- ```
+
+
+ Make sure `build` and `start` scripts are present in your `package.json` file:
+
+ ```json package.json icon="file-json"
+ {
+ "scripts": {
+ "build": "bun --bun vite build", // [!code ++]
+ // The .output files are created by Nitro when you run `bun run build`.
+ // Not necessary when deploying to Vercel.
+ "start": "bun run .output/server/index.mjs" // [!code ++]
+ }
+ }
+ ```
-
- You do **not** need the custom `start` script when deploying to Vercel.
-
+
+ You do **not** need the custom `start` script when deploying to Vercel.
+
-
-
- Check out one of our guides to deploy your app to a hosting provider.
+
+
+ Check out one of our guides to deploy your app to a hosting provider.
-
- When deploying to Vercel, you can either add the `"bunVersion": "1.x"` to your `vercel.json` file, or add it to the `nitro` config in your `vite.config.ts` file:
+
+ When deploying to Vercel, you can either add the `"bunVersion": "1.x"` to your `vercel.json` file, or add it to the `nitro` config in your `vite.config.ts` file:
-
- Do **not** use the `bun` Nitro preset when deploying to Vercel.
-
+
+ Do **not** use the `bun` Nitro preset when deploying to Vercel.
+
- ```ts vite.config.ts icon="/icons/typescript.svg"
- export default defineConfig({
- plugins: [
- tanstackStart(),
- nitro({
- preset: "bun", // [!code --]
- vercel: { // [!code ++]
- functions: { // [!code ++]
- runtime: "bun1.x", // [!code ++]
+ ```ts vite.config.ts icon="/icons/typescript.svg"
+ export default defineConfig({
+ plugins: [
+ tanstackStart(),
+ nitro({
+ preset: "bun", // [!code --]
+ vercel: { // [!code ++]
+ functions: { // [!code ++]
+ runtime: "bun1.x", // [!code ++]
+ }, // [!code ++]
}, // [!code ++]
- }, // [!code ++]
- }),
- ],
- });
- ```
+ }),
+ ],
+ });
+ ```
+
+
+
+
+
+
+
+ This custom server implementation is based on [TanStack's Bun template](https://github.com/TanStack/router/blob/main/examples/react/start-bun/server.ts). It provides fine-grained control over static asset serving, including configurable memory management that preloads small files into memory for fast serving while serving larger files on-demand. This approach is useful when you need precise control over resource usage and asset loading behavior in production deployments.
-
-
- Deploy on Vercel
-
-
- Deploy on Render
-
-
- Deploy on Railway
-
-
- Deploy on DigitalOcean
-
-
- Deploy on AWS Lambda
-
-
- Deploy on Google Cloud Run
-
-
+
+
+ Create a `server.ts` file in your project root with the following custom server implementation:
-
-
+ ```ts server.ts icon="/icons/typescript.svg" expandable
+ /**
+ * TanStack Start Production Server with Bun
+ *
+ * A high-performance production server for TanStack Start applications that
+ * implements intelligent static asset loading with configurable memory management.
+ *
+ * Features:
+ * - Hybrid loading strategy (preload small files, serve large files on-demand)
+ * - Configurable file filtering with include/exclude patterns
+ * - Memory-efficient response generation
+ * - Production-ready caching headers
+ *
+ * Environment Variables:
+ *
+ * PORT (number)
+ * - Server port number
+ * - Default: 3000
+ *
+ * ASSET_PRELOAD_MAX_SIZE (number)
+ * - Maximum file size in bytes to preload into memory
+ * - Files larger than this will be served on-demand from disk
+ * - Default: 5242880 (5MB)
+ * - Example: ASSET_PRELOAD_MAX_SIZE=5242880 (5MB)
+ *
+ * ASSET_PRELOAD_INCLUDE_PATTERNS (string)
+ * - Comma-separated list of glob patterns for files to include
+ * - If specified, only matching files are eligible for preloading
+ * - Patterns are matched against filenames only, not full paths
+ * - Example: ASSET_PRELOAD_INCLUDE_PATTERNS="*.js,*.css,*.woff2"
+ *
+ * ASSET_PRELOAD_EXCLUDE_PATTERNS (string)
+ * - Comma-separated list of glob patterns for files to exclude
+ * - Applied after include patterns
+ * - Patterns are matched against filenames only, not full paths
+ * - Example: ASSET_PRELOAD_EXCLUDE_PATTERNS="*.map,*.txt"
+ *
+ * ASSET_PRELOAD_VERBOSE_LOGGING (boolean)
+ * - Enable detailed logging of loaded and skipped files
+ * - Default: false
+ * - Set to "true" to enable verbose output
+ *
+ * ASSET_PRELOAD_ENABLE_ETAG (boolean)
+ * - Enable ETag generation for preloaded assets
+ * - Default: true
+ * - Set to "false" to disable ETag support
+ *
+ * ASSET_PRELOAD_ENABLE_GZIP (boolean)
+ * - Enable Gzip compression for eligible assets
+ * - Default: true
+ * - Set to "false" to disable Gzip compression
+ *
+ * ASSET_PRELOAD_GZIP_MIN_SIZE (number)
+ * - Minimum file size in bytes required for Gzip compression
+ * - Files smaller than this will not be compressed
+ * - Default: 1024 (1KB)
+ *
+ * ASSET_PRELOAD_GZIP_MIME_TYPES (string)
+ * - Comma-separated list of MIME types eligible for Gzip compression
+ * - Supports partial matching for types ending with "/"
+ * - Default: text/,application/javascript,application/json,application/xml,image/svg+xml
+ *
+ * Usage:
+ * bun run server.ts
+ */
+
+ import path from 'node:path'
+
+ // Configuration
+ const SERVER_PORT = Number(process.env.PORT ?? 3000)
+ const CLIENT_DIRECTORY = './dist/client'
+ const SERVER_ENTRY_POINT = './dist/server/server.js'
+
+ // Logging utilities for professional output
+ const log = {
+ info: (message: string) => {
+ console.log(`[INFO] ${message}`)
+ },
+ success: (message: string) => {
+ console.log(`[SUCCESS] ${message}`)
+ },
+ warning: (message: string) => {
+ console.log(`[WARNING] ${message}`)
+ },
+ error: (message: string) => {
+ console.log(`[ERROR] ${message}`)
+ },
+ header: (message: string) => {
+ console.log(`\n${message}\n`)
+ },
+ }
+
+ // Preloading configuration from environment variables
+ const MAX_PRELOAD_BYTES = Number(
+ process.env.ASSET_PRELOAD_MAX_SIZE ?? 5 * 1024 * 1024, // 5MB default
+ )
+
+ // Parse comma-separated include patterns (no defaults)
+ const INCLUDE_PATTERNS = (process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? '')
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean)
+ .map((pattern: string) => convertGlobToRegExp(pattern))
+
+ // Parse comma-separated exclude patterns (no defaults)
+ const EXCLUDE_PATTERNS = (process.env.ASSET_PRELOAD_EXCLUDE_PATTERNS ?? '')
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean)
+ .map((pattern: string) => convertGlobToRegExp(pattern))
+
+ // Verbose logging flag
+ const VERBOSE = process.env.ASSET_PRELOAD_VERBOSE_LOGGING === 'true'
+
+ // Optional ETag feature
+ const ENABLE_ETAG = (process.env.ASSET_PRELOAD_ENABLE_ETAG ?? 'true') === 'true'
+
+ // Optional Gzip feature
+ const ENABLE_GZIP = (process.env.ASSET_PRELOAD_ENABLE_GZIP ?? 'true') === 'true'
+ const GZIP_MIN_BYTES = Number(process.env.ASSET_PRELOAD_GZIP_MIN_SIZE ?? 1024) // 1KB
+ const GZIP_TYPES = (
+ process.env.ASSET_PRELOAD_GZIP_MIME_TYPES ??
+ 'text/,application/javascript,application/json,application/xml,image/svg+xml'
+ )
+ .split(',')
+ .map((v) => v.trim())
+ .filter(Boolean)
+
+ /**
+ * Convert a simple glob pattern to a regular expression
+ * Supports * wildcard for matching any characters
+ */
+ function convertGlobToRegExp(globPattern: string): RegExp {
+ // Escape regex special chars except *, then replace * with .*
+ const escapedPattern = globPattern
+ .replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&')
+ .replace(/\*/g, '.*')
+ return new RegExp(`^${escapedPattern}$`, 'i')
+ }
+
+ /**
+ * Compute ETag for a given data buffer
+ */
+ function computeEtag(data: Uint8Array): string {
+ const hash = Bun.hash(data)
+ return `W/"${hash.toString(16)}-${data.byteLength.toString()}"`
+ }
+
+ /**
+ * Metadata for preloaded static assets
+ */
+ interface AssetMetadata {
+ route: string
+ size: number
+ type: string
+ }
+
+ /**
+ * In-memory asset with ETag and Gzip support
+ */
+ interface InMemoryAsset {
+ raw: Uint8Array
+ gz?: Uint8Array
+ etag?: string
+ type: string
+ immutable: boolean
+ size: number
+ }
+
+ /**
+ * Result of static asset preloading process
+ */
+ interface PreloadResult {
+ routes: Record Response | Promise>
+ loaded: AssetMetadata[]
+ skipped: AssetMetadata[]
+ }
+
+ /**
+ * Check if a file is eligible for preloading based on configured patterns
+ */
+ function isFileEligibleForPreloading(relativePath: string): boolean {
+ const fileName = relativePath.split(/[/\\]/).pop() ?? relativePath
+
+ // If include patterns are specified, file must match at least one
+ if (INCLUDE_PATTERNS.length > 0) {
+ if (!INCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
+ return false
+ }
+ }
+
+ // If exclude patterns are specified, file must not match any
+ if (EXCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
+ return false
+ }
+
+ return true
+ }
+
+ /**
+ * Check if a MIME type is compressible
+ */
+ function isMimeTypeCompressible(mimeType: string): boolean {
+ return GZIP_TYPES.some((type) =>
+ type.endsWith('/') ? mimeType.startsWith(type) : mimeType === type,
+ )
+ }
+
+ /**
+ * Conditionally compress data based on size and MIME type
+ */
+ function compressDataIfAppropriate(
+ data: Uint8Array,
+ mimeType: string,
+ ): Uint8Array | undefined {
+ if (!ENABLE_GZIP) return undefined
+ if (data.byteLength < GZIP_MIN_BYTES) return undefined
+ if (!isMimeTypeCompressible(mimeType)) return undefined
+ try {
+ return Bun.gzipSync(data.buffer as ArrayBuffer)
+ } catch {
+ return undefined
+ }
+ }
+
+ /**
+ * Create response handler function with ETag and Gzip support
+ */
+ function createResponseHandler(
+ asset: InMemoryAsset,
+ ): (req: Request) => Response {
+ return (req: Request) => {
+ const headers: Record = {
+ 'Content-Type': asset.type,
+ 'Cache-Control': asset.immutable
+ ? 'public, max-age=31536000, immutable'
+ : 'public, max-age=3600',
+ }
+
+ if (ENABLE_ETAG && asset.etag) {
+ const ifNone = req.headers.get('if-none-match')
+ if (ifNone && ifNone === asset.etag) {
+ return new Response(null, {
+ status: 304,
+ headers: { ETag: asset.etag },
+ })
+ }
+ headers.ETag = asset.etag
+ }
+
+ if (
+ ENABLE_GZIP &&
+ asset.gz &&
+ req.headers.get('accept-encoding')?.includes('gzip')
+ ) {
+ headers['Content-Encoding'] = 'gzip'
+ headers['Content-Length'] = String(asset.gz.byteLength)
+ const gzCopy = new Uint8Array(asset.gz)
+ return new Response(gzCopy, { status: 200, headers })
+ }
+
+ headers['Content-Length'] = String(asset.raw.byteLength)
+ const rawCopy = new Uint8Array(asset.raw)
+ return new Response(rawCopy, { status: 200, headers })
+ }
+ }
+
+ /**
+ * Create composite glob pattern from include patterns
+ */
+ function createCompositeGlobPattern(): Bun.Glob {
+ const raw = (process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? '')
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean)
+ if (raw.length === 0) return new Bun.Glob('**/*')
+ if (raw.length === 1) return new Bun.Glob(raw[0])
+ return new Bun.Glob(`{${raw.join(',')}}`)
+ }
+
+ /**
+ * Initialize static routes with intelligent preloading strategy
+ * Small files are loaded into memory, large files are served on-demand
+ */
+ async function initializeStaticRoutes(
+ clientDirectory: string,
+ ): Promise {
+ const routes: Record Response | Promise> =
+ {}
+ const loaded: AssetMetadata[] = []
+ const skipped: AssetMetadata[] = []
+
+ log.info(`Loading static assets from ${clientDirectory}...`)
+ if (VERBOSE) {
+ console.log(
+ `Max preload size: ${(MAX_PRELOAD_BYTES / 1024 / 1024).toFixed(2)} MB`,
+ )
+ if (INCLUDE_PATTERNS.length > 0) {
+ console.log(
+ `Include patterns: ${process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? ''}`,
+ )
+ }
+ if (EXCLUDE_PATTERNS.length > 0) {
+ console.log(
+ `Exclude patterns: ${process.env.ASSET_PRELOAD_EXCLUDE_PATTERNS ?? ''}`,
+ )
+ }
+ }
+
+ let totalPreloadedBytes = 0
+
+ try {
+ const glob = createCompositeGlobPattern()
+ for await (const relativePath of glob.scan({ cwd: clientDirectory })) {
+ const filepath = path.join(clientDirectory, relativePath)
+ const route = `/${relativePath.split(path.sep).join(path.posix.sep)}`
+
+ try {
+ // Get file metadata
+ const file = Bun.file(filepath)
+
+ // Skip if file doesn't exist or is empty
+ if (!(await file.exists()) || file.size === 0) {
+ continue
+ }
+
+ const metadata: AssetMetadata = {
+ route,
+ size: file.size,
+ type: file.type || 'application/octet-stream',
+ }
+
+ // Determine if file should be preloaded
+ const matchesPattern = isFileEligibleForPreloading(relativePath)
+ const withinSizeLimit = file.size <= MAX_PRELOAD_BYTES
+
+ if (matchesPattern && withinSizeLimit) {
+ // Preload small files into memory with ETag and Gzip support
+ const bytes = new Uint8Array(await file.arrayBuffer())
+ const gz = compressDataIfAppropriate(bytes, metadata.type)
+ const etag = ENABLE_ETAG ? computeEtag(bytes) : undefined
+ const asset: InMemoryAsset = {
+ raw: bytes,
+ gz,
+ etag,
+ type: metadata.type,
+ immutable: true,
+ size: bytes.byteLength,
+ }
+ routes[route] = createResponseHandler(asset)
+
+ loaded.push({ ...metadata, size: bytes.byteLength })
+ totalPreloadedBytes += bytes.byteLength
+ } else {
+ // Serve large or filtered files on-demand
+ routes[route] = () => {
+ const fileOnDemand = Bun.file(filepath)
+ return new Response(fileOnDemand, {
+ headers: {
+ 'Content-Type': metadata.type,
+ 'Cache-Control': 'public, max-age=3600',
+ },
+ })
+ }
+
+ skipped.push(metadata)
+ }
+ } catch (error: unknown) {
+ if (error instanceof Error && error.name !== 'EISDIR') {
+ log.error(`Failed to load ${filepath}: ${error.message}`)
+ }
+ }
+ }
+
+ // Show detailed file overview only when verbose mode is enabled
+ if (VERBOSE && (loaded.length > 0 || skipped.length > 0)) {
+ const allFiles = [...loaded, ...skipped].sort((a, b) =>
+ a.route.localeCompare(b.route),
+ )
+
+ // Calculate max path length for alignment
+ const maxPathLength = Math.min(
+ Math.max(...allFiles.map((f) => f.route.length)),
+ 60,
+ )
+
+ // Format file size with KB and actual gzip size
+ const formatFileSize = (bytes: number, gzBytes?: number) => {
+ const kb = bytes / 1024
+ const sizeStr = kb < 100 ? kb.toFixed(2) : kb.toFixed(1)
+
+ if (gzBytes !== undefined) {
+ const gzKb = gzBytes / 1024
+ const gzStr = gzKb < 100 ? gzKb.toFixed(2) : gzKb.toFixed(1)
+ return {
+ size: sizeStr,
+ gzip: gzStr,
+ }
+ }
+
+ // Rough gzip estimation (typically 30-70% compression) if no actual gzip data
+ const gzipKb = kb * 0.35
+ return {
+ size: sizeStr,
+ gzip: gzipKb < 100 ? gzipKb.toFixed(2) : gzipKb.toFixed(1),
+ }
+ }
+
+ if (loaded.length > 0) {
+ console.log('\nš Preloaded into memory:')
+ console.log(
+ 'Path ā Size ā Gzip Size',
+ )
+ loaded
+ .sort((a, b) => a.route.localeCompare(b.route))
+ .forEach((file) => {
+ const { size, gzip } = formatFileSize(file.size)
+ const paddedPath = file.route.padEnd(maxPathLength)
+ const sizeStr = `${size.padStart(7)} kB`
+ const gzipStr = `${gzip.padStart(7)} kB`
+ console.log(`${paddedPath} ā ${sizeStr} ā ${gzipStr}`)
+ })
+ }
+
+ if (skipped.length > 0) {
+ console.log('\nš¾ Served on-demand:')
+ console.log(
+ 'Path ā Size ā Gzip Size',
+ )
+ skipped
+ .sort((a, b) => a.route.localeCompare(b.route))
+ .forEach((file) => {
+ const { size, gzip } = formatFileSize(file.size)
+ const paddedPath = file.route.padEnd(maxPathLength)
+ const sizeStr = `${size.padStart(7)} kB`
+ const gzipStr = `${gzip.padStart(7)} kB`
+ console.log(`${paddedPath} ā ${sizeStr} ā ${gzipStr}`)
+ })
+ }
+ }
+
+ // Show detailed verbose info if enabled
+ if (VERBOSE) {
+ if (loaded.length > 0 || skipped.length > 0) {
+ const allFiles = [...loaded, ...skipped].sort((a, b) =>
+ a.route.localeCompare(b.route),
+ )
+ console.log('\nš Detailed file information:')
+ console.log(
+ 'Status ā Path ā MIME Type ā Reason',
+ )
+ allFiles.forEach((file) => {
+ const isPreloaded = loaded.includes(file)
+ const status = isPreloaded ? 'MEMORY' : 'ON-DEMAND'
+ const reason =
+ !isPreloaded && file.size > MAX_PRELOAD_BYTES
+ ? 'too large'
+ : !isPreloaded
+ ? 'filtered'
+ : 'preloaded'
+ const route =
+ file.route.length > 30
+ ? file.route.substring(0, 27) + '...'
+ : file.route
+ console.log(
+ `${status.padEnd(12)} ā ${route.padEnd(30)} ā ${file.type.padEnd(28)} ā ${reason.padEnd(10)}`,
+ )
+ })
+ } else {
+ console.log('\nš No files found to display')
+ }
+ }
+
+ // Log summary after the file list
+ console.log() // Empty line for separation
+ if (loaded.length > 0) {
+ log.success(
+ `Preloaded ${String(loaded.length)} files (${(totalPreloadedBytes / 1024 / 1024).toFixed(2)} MB) into memory`,
+ )
+ } else {
+ log.info('No files preloaded into memory')
+ }
+
+ if (skipped.length > 0) {
+ const tooLarge = skipped.filter((f) => f.size > MAX_PRELOAD_BYTES).length
+ const filtered = skipped.length - tooLarge
+ log.info(
+ `${String(skipped.length)} files will be served on-demand (${String(tooLarge)} too large, ${String(filtered)} filtered)`,
+ )
+ }
+ } catch (error) {
+ log.error(
+ `Failed to load static files from ${clientDirectory}: ${String(error)}`,
+ )
+ }
+
+ return { routes, loaded, skipped }
+ }
+
+ /**
+ * Initialize the server
+ */
+ async function initializeServer() {
+ log.header('Starting Production Server')
+
+ // Load TanStack Start server handler
+ let handler: { fetch: (request: Request) => Response | Promise }
+ try {
+ const serverModule = (await import(SERVER_ENTRY_POINT)) as {
+ default: { fetch: (request: Request) => Response | Promise }
+ }
+ handler = serverModule.default
+ log.success('TanStack Start application handler initialized')
+ } catch (error) {
+ log.error(`Failed to load server handler: ${String(error)}`)
+ process.exit(1)
+ }
+
+ // Build static routes with intelligent preloading
+ const { routes } = await initializeStaticRoutes(CLIENT_DIRECTORY)
+
+ // Create Bun server
+ const server = Bun.serve({
+ port: SERVER_PORT,
+
+ routes: {
+ // Serve static assets (preloaded or on-demand)
+ ...routes,
+
+ // Fallback to TanStack Start handler for all other routes
+ '/*': (req: Request) => {
+ try {
+ return handler.fetch(req)
+ } catch (error) {
+ log.error(`Server handler error: ${String(error)}`)
+ return new Response('Internal Server Error', { status: 500 })
+ }
+ },
+ },
+
+ // Global error handler
+ error(error) {
+ log.error(
+ `Uncaught server error: ${error instanceof Error ? error.message : String(error)}`,
+ )
+ return new Response('Internal Server Error', { status: 500 })
+ },
+ })
+
+ log.success(`Server listening on http://localhost:${String(server.port)}`)
+ }
+
+ // Initialize the server
+ initializeServer().catch((error: unknown) => {
+ log.error(`Failed to start server: ${String(error)}`)
+ process.exit(1)
+ })
+ ```
+
+
+
+ Add a `start` script to run the custom server:
+
+ ```json package.json icon="file-json"
+ {
+ "scripts": {
+ "build": "bun --bun vite build",
+ "start": "bun run server.ts" // [!code ++]
+ }
+ }
+ ```
+
+
+
+ Build your application and start the server:
+
+ ```sh terminal icon="terminal"
+ bun run build
+ bun run start
+ ```
+
+ The server will start on port 3000 by default (configurable via `PORT` environment variable).
+
+
+
+
+
+
+
+
+
+ Deploy on Vercel
+
+
+ Deploy on Render
+
+
+ Deploy on Railway
+
+
+ Deploy on DigitalOcean
+
+
+ Deploy on AWS Lambda
+
+
+ Deploy on Google Cloud Run
+
+
+
+---
[ā See TanStack Start's official documentation](https://tanstack.com/start/latest/docs/framework/react/guide/hosting) for more information on hosting.
diff --git a/docs/snippets/guides.jsx b/docs/snippets/guides.jsx
index 248814ef79..b17af36b57 100644
--- a/docs/snippets/guides.jsx
+++ b/docs/snippets/guides.jsx
@@ -5,6 +5,12 @@ export const GuidesList = () => {
blurb: "A collection of code samples and walkthroughs for performing common tasks with Bun.",
},
featured: [
+ {
+ category: "Ecosystem",
+ title: "Use Tanstack Start with Bun",
+ href: "/guides/ecosystem/tanstack-start",
+ cta: "View guide",
+ },
{
category: "Ecosystem",
title: "Build a frontend using Vite and Bun",
@@ -35,12 +41,6 @@ export const GuidesList = () => {
href: "/guides/websocket/simple",
cta: "View guide",
},
- {
- category: "Reading files",
- title: "Read a file as a string",
- href: "/guides/read-file/string",
- cta: "View guide",
- },
],
categories: [
{
@@ -135,6 +135,7 @@ export const GuidesList = () => {
{ title: "Build an app with Qwik and Bun", href: "/guides/ecosystem/qwik" },
{ title: "Build an app with Astro and Bun", href: "/guides/ecosystem/astro" },
{ title: "Build an app with Remix and Bun", href: "/guides/ecosystem/remix" },
+ { title: "Use TanStack Start with Bun", href: "/guides/ecosystem/tanstack-start" },
{ title: "Run Bun as a daemon with systemd", href: "/guides/ecosystem/systemd" },
{ title: "Build an app with Next.js and Bun", href: "/guides/ecosystem/nextjs" },
{ title: "Build an app with SvelteKit and Bun", href: "/guides/ecosystem/sveltekit" },