clientshell
Inject public runtime config into compiled frontend apps at container startup—no rebuilding for each environment.
clientshell provides a typed browser API for configuration and an ultra-fast Go injector that bridges the gap between static builds (Vite, Webpack) and dynamic environment variables.
The Problem
Modern frontend tools pre-compile environment variables into the binary build (e.g. VITE_API_URL is replaced with standard strings). This forces a complete rebuild (CI/CD) when moving your application between staging and production environments because the environment variables are baked in.
The Solution
clientshell fixes this without resorting to unsafe, un-typed window polling.
It accomplishes this via:
- Defining a schema in TypeScript (via
@clientshell/coreor@clientshell/zod). - Generating a manifest during the build phase (integrates natively with Vite, Webpack, and Rollup).
- Injecting variables at runtime using an ultra-fast Go binary (
/env-config.js). - Providing a typed API in the browser that allows safe read-modes with zero extra dependencies in your final production bundle.
Quick Start
1. Install Dependencies
You'll need the core library and the plugin for your specific bundler. For Vite:
pnpm add @clientshell/core
pnpm add -D @clientshell/vite2. Define your schema
Create an env.schema.ts file in your source code.
import { defineSchema, string, boolean } from "@clientshell/core";
export const clientEnvSchema = defineSchema({
API_URL: string({ required: true }),
ENABLE_BETA: boolean({ defaultValue: false }),
});3. Configure your bundler
Add the plugin to your bundler configuration.
// vite.config.ts
import { defineConfig } from 'vite';
import { clientshellPlugin } from "@clientshell/vite";
import { clientEnvSchema } from "./src/env.schema";
export default defineConfig({
plugins: [
clientshellPlugin({ schema: clientEnvSchema })
]
});4. Read at runtime
Read your environment variables in your frontend code safely.
import { readEnvFromShape } from "@clientshell/core";
import { clientEnvSchema } from "./env.schema";
const env = readEnvFromShape(clientEnvSchema);
// Typed as a string!
console.log(env.API_URL); 