Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the sixth module of our course. We've covered a lot of ground, from the fundamentals of SSR with Tanstack Router to advanced data management techniques. Now, we'll focus on the final, crucial stage: getting your application into the hands of users.
This module, "Production Deployment and Monitoring," will guide you through building, deploying, and maintaining your Tanstack Start application.
Today's lesson addresses the first step in this process. Our goal is to generate and inspect a production-ready build of a Tanstack Start application. We will explore the build command, analyze the structure of the output for an SSR application, and learn how to configure the build process for different environments. This will lay the essential groundwork for our next lesson on deploying the application.
1. Generating the Production Build
At the heart of preparing your application for production is the build process. Tanstack Start uses Vite as its build tool. For anyone with front-end experience, the concept of a build step is familiar: it transforms your development code (like .tsx and .ts files) into optimized, static assets that a browser and server can understand.
In a typical Tanstack Start project, your package.json will contain a script for this, usually named build.
// package.json (example)
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "node dist/server"
}
Running npm run build executes the vite build command, which bundles your application for production.
To see a general overview of this process in a standard Vite project, please watch the following short segment. It will demonstrate the build command and the resulting output directory.
Vite Crash Course – Frontend Build Tool
This clip from the 'Vite Crash Course' on the freeCodeCamp.org channel provides a clear, concise demonstration of running a production build with Vite.
Watch the section 'Building for Production' from 25:24 to 27:15. Pay attention to the command used and the name of the output directory that is created.
As the video shows, the command creates a dist directory containing the production-ready files. However, for an SSR application like one built with Tanstack Start, the contents of this directory are more complex than for a simple Client-Side Rendered (CSR) app.
2. Anatomy of a Tanstack Start Build
When you build a Tanstack Start application, Vite performs two separate builds: one for the client and one for the server. This results in a specific directory structure designed to support Server-Side Rendering.
Let's inspect the typical output in the dist/ directory:
dist/
├── client/
│ ├── assets/
│ │ ├── index-*.js // Client-side JS bundle
│ │ └── index-*.css // Client-side CSS
│ └── index.html // The HTML shell (often a template)
│
├── server/
│ └── index.js // The server entry point, runs in Node.js/Bun
│
└── ssr-manifest.json // Maps modules to their client-side chunks
Let's break down what each part does:
-
dist/client/: This directory contains all the static assets that will be sent to the browser.- The JavaScript bundles in
assets/are responsible for hydration—the process of "re-attaching" React to the server-rendered HTML, making the page interactive. - The
index.htmlfile serves as the template that the server will populate with rendered content.
- The JavaScript bundles in
-
dist/server/: This contains the server-side logic of your application, bundled into a single entry point (e.g.,index.js). When you run your production server, you are executing this file. Its job is to:- Receive an incoming HTTP request.
- Match the request URL to a route.
- Execute the corresponding route's
loaderfunction to fetch data. - Render the React components to an HTML string.
- Inject the rendered HTML, data, and necessary
<script>tags into thedist/client/index.htmltemplate. - Send the complete HTML document back to the client.
-
ssr-manifest.json: This crucial file is a map. The server-side renderer uses it to figure out which specific JavaScript and CSS files from thedist/client/assets/directory need to be included in the final HTML for a given route. This enables efficient code-splitting, so the client only downloads the code it needs for the current page.
3. Configuring the Build
While the default build settings are sensible, production applications often require customization, especially regarding environment variables.
Modes and Environment Variables
Vite has a powerful system for managing environment variables using .env files and "modes". A mode is a string (like production or staging) that can be used to load a specific set of environment variables.
- By default,
vite buildruns inproductionmode. - You can specify a different mode with the
--modeflag, e.g.,vite build --mode staging.
This allows you to have different configurations for different environments. For example:
.env: Variables loaded in all modes..env.production: Variables that override.envwhen inproductionmode..env.staging: Variables that override.envwhen instagingmode.
To understand this system in detail, please read the following sections from the official Vite documentation.
This resource, 'Env Variables and Modes' from the Vite documentation, is essential for understanding how to manage configuration across different environments.
Please read the following sections: 'Built-in Constants': To understand variables like import.meta.env.PROD and import.meta.env.SSR. 'Env Variables' and '.env Files': Focus on the VITE_ prefix requirement for exposing variables to the client and the loading priority of different .env files. 'Modes': To see how to use the --mode flag for builds like staging. 'NODE_ENV and Modes': To grasp the important distinction between these two concepts.
A critical security takeaway from this reading is that only environment variables prefixed with VITE_ are exposed to your client-side code. Never put secrets (like API keys or database passwords) in a variable with a VITE_ prefix, as they will be included in the client bundle. Server-only secrets should be accessed directly via process.env within your server-side code (e.g., in route loaders or server functions), which will not be exposed to the client.
Customizing the Build Output
You can also customize the build output directly in your vite.config.ts file. For instance, you might want to change the output directory from dist to something else.
The following video clip demonstrates this simple change.
Vite Crash Course – Frontend Build Tool
This next clip from the same 'Vite Crash Course' shows how to modify the vite.config.js file to change the build output directory.
Watch the section 'Configuring Vite Build Output' from 28:34 to 29:52. This illustrates how the build.outDir option works.
For more advanced customizations, Vite allows you to directly access the underlying Rollup configuration via the build.rollupOptions object in your vite.config.ts. This provides a powerful escape hatch for complex scenarios, leveraging your existing knowledge of modern JavaScript bundling. You can read more about this in the "Customizing the Build" section of the Vite documentation on "Building for Production".
4. Running the Production Build Locally
After running npm run build, you'll want to test the production version locally.
With a client-side rendered app, you might use vite preview, which serves the static files from the dist folder. This is not correct for an SSR application. The vite preview command only serves static files; it doesn't run your server-side logic.
To properly run your production SSR build, you must execute the server entry point. A conventional Tanstack Start project includes a start script in package.json for this purpose:
// package.json (example)
"scripts": {
// ...
"build": "vite build",
"start": "node dist/server" // or `bun dist/server`
}
Running npm start will start a web server using your production-ready server bundle. This server will perform the full SSR process for every request, just as it would in a live production environment. You can then access http://localhost:3000 (or another configured port) in your browser to see the fully-rendered application.
Key Takeaways
- Build Command:
npm run build(orvite build) generates a production-ready version of your application. - SSR Build Structure: The build output is split into a
clientdirectory (for browser assets and hydration) and aserverdirectory (for server-side rendering logic). - SSR Manifest: The
ssr-manifest.jsonfile maps server-side modules to their client-side asset dependencies, enabling efficient code-splitting. - Configuration: Build behavior is controlled through
vite.config.tsand environment variables managed via.envfiles and Vite'smodesystem. - Security: Only variables prefixed with
VITE_are exposed to the client. Keep all secrets in server-only environment variables. - Local Preview: Use
npm start(or the equivalent command to run yourdist/serverentry point) to test your production SSR build locally, notvite preview.
In our next lesson, we will take the build artifacts we've learned to generate and inspect today and deploy them to a serverless platform, Vercel, bringing your application to the web.