The Unstract frontend is built with React 18 and uses Vite as the build tool for fast development and optimized production builds.
Migration Note: This project was migrated from Create React App to Vite 6 on 2025-10-19, and upgraded to Vite 7 on 2026-02-04. See VITE_MIGRATION.md for details.
- Bun: Version 1.0.0 or higher (recommended)
- Node.js: Version 20.19.0 or higher (for compatibility)
git clone https://github.com/Zipstack/unstract.git
cd unstract/frontendbun installCopy the sample environment file and configure it:
cp sample.env .envImportant: All environment variables must use the VITE_ prefix (not REACT_APP_):
VITE_BACKEND_URL=http://frontend.unstract.localhost:8081
VITE_ENABLE_POSTHOG=false
VITE_FAVICON_PATH=/path/to/custom/favicon.ico
VITE_CUSTOM_LOGO_URL=/path/to/custom/logo.svgbun startOr use the Vite-specific command:
bun run devThe application will be available at http://localhost:3000.
Hot Module Replacement (HMR) is enabled by default - the page will automatically update when you make changes without losing component state.
- Biome - Linter and formatter: https://marketplace.visualstudio.com/items?itemName=biomejs.biome
Starts the Vite development server with Hot Module Replacement (HMR).
- URL: http://localhost:3000
- Features:
- Near-instant server start
- Lightning-fast HMR (updates without full page reload)
- On-demand compilation
- Error overlay for build errors
Locally preview the production build before deployment.
- URL: http://localhost:4173
- Use case: Test production build locally
Builds the application for production to the build/ folder.
Build Features:
- Optimized production bundle with tree-shaking
- Code splitting for better caching
- Minified output with hashed filenames
- Vendor chunk splitting for React, Ant Design, and PDF libraries
- Source maps generation (configurable)
Output: Production-ready static files in build/ directory
Runs tests using Vitest in watch mode.
Features:
- Fast test execution
- Component testing support
- Compatible with existing Jest tests
- Watch mode for development
The project uses Biome for linting and formatting - a fast, unified tool that replaces ESLint and Prettier.
Available commands:
bun run lint- Check for linting errorsbun run lint:fix- Auto-fix linting errorsbun run format- Check formatting issuesbun run format:fix- Auto-fix formatting issuesbun run check- Run both linting and formatting checksbun run check:fix- Auto-fix both linting and formatting issuesbun run lint:all- Run all Biome checks with auto-fix on all filesbun run lint:changed- Run Biome checks only on changed files
Configuration: Biome is configured via biome.json in the frontend directory.
Note: Make sure to run bun install first to install the required dependencies.
Vite uses a different approach to environment variables compared to Create React App:
All custom environment variables must use the VITE_ prefix:
// ✅ Correct
console.log(import.meta.env.VITE_BACKEND_URL);
// ❌ Wrong (CRA style - no longer works)
console.log(process.env.REACT_APP_BACKEND_URL);Vite provides these built-in variables:
import.meta.env.MODE:'development'or'production'import.meta.env.DEV:truein developmentimport.meta.env.PROD:truein productionimport.meta.env.BASE_URL: Base URL of the app
Vite automatically loads environment files:
.env: Loaded in all cases.env.local: Loaded in all cases (gitignored).env.[mode]: Only loaded in specified mode.env.[mode].local: Only loaded in specified mode (gitignored)
Note: Restart the dev server after changing .env files.
The project is configured via vite.config.js in the root directory:
Key features:
- Proxy: API calls to
/apiare proxied to the backend - HMR: Hot Module Replacement with polling for Docker compatibility
- Build optimization: Manual chunk splitting for better caching
- JSX in .js files: esbuild configured to handle JSX syntax in
.jsfiles
Backend API calls are automatically proxied in development:
// vite.config.js
server: {
proxy: {
'/api': {
target: env.VITE_BACKEND_URL,
changeOrigin: true,
secure: false,
},
},
}Usage in code:
// These calls will be proxied to the backend
axios.get('/api/v1/users');
fetch('/api/v1/workflows');React Strict Mode is enabled by default in development, which:
- Mounts components twice to detect side effects
- Helps identify unsafe lifecycles and deprecated APIs
- Ensures components are resilient to remounting
This behavior only occurs in development and helps maintain component quality.
Reference static assets from the public/ directory:
// ✅ Correct
<img src="/images/logo.png" alt="Logo" />
// ❌ Wrong (CRA style)
<img src={`${process.env.PUBLIC_URL}/images/logo.png`} alt="Logo" />Use dynamic imports for code splitting:
const Dashboard = lazy(() => import('./pages/Dashboard'));Vite automatically creates separate chunks for dynamically imported modules.
The frontend is fully containerized for both development and production environments.
From the project root:
# Start all services (from repository root)
./run-platform.sh
# Or manually with docker compose
docker compose upThe frontend will be available at http://frontend.unstract.localhost.
The development Dockerfile is located at docker/dockerfiles/frontend.Dockerfile.
Key features:
- Vite dev server with HMR enabled
- File watching with polling for Docker volume compatibility
- Hot reload when source files change
- Proxy configuration for backend API calls
Environment variables for Docker:
# Development
VITE_BACKEND_URL=http://frontend.unstract.localhost:8081
WDS_SOCKET_PORT=3000
CHOKIDAR_USEPOLLING=trueProduction builds use NGINX to serve the optimized static files:
# Build production image
docker compose -f docker-compose.build.yaml build frontend
# The build process:
# 1. bun install dependencies
# 2. vite build (outputs to build/ directory)
# 3. Copy build/ to NGINX html directory
# 4. Inject runtime config script for dynamic environment variablesProduction build features:
- Optimized bundle with tree-shaking
- Vendor chunk splitting (React, Ant Design, PDF libraries)
- Minified assets with content hashing
- Runtime configuration injection for dynamic environment variables
The frontend supports runtime environment variable injection (without rebuilding):
- Environment variables are read from Docker environment
generate-runtime-config.shcreates/config/runtime-config.js- Script is injected into
index.htmlafter build - JavaScript reads from
window.RUNTIME_CONFIG
Supported runtime variables:
VITE_FAVICON_PATH/REACT_APP_FAVICON_PATH(backward compatibility)VITE_CUSTOM_LOGO_URL/REACT_APP_CUSTOM_LOGO_URL(backward compatibility)
Hot Module Replacement works in Docker through:
- Polling: File watching uses polling instead of native filesystem events
- Port configuration: HMR WebSocket port matches the exposed container port
- Host binding: Vite server binds to
0.0.0.0for external access
Configuration in vite.config.js:
server: {
host: '0.0.0.0',
port: 3000,
watch: {
usePolling: true,
interval: 100,
},
hmr: {
port: 3000,
clientPort: env.WDS_SOCKET_PORT ? Number(env.WDS_SOCKET_PORT) : 3000,
},
}The build configuration includes intelligent chunk splitting:
Vendor chunks:
react-vendor: React, React DOM, React Routerantd-vendor: Ant Design and iconspdf-vendor: PDF viewer libraries
Benefits:
- Better browser caching (vendor code changes less frequently)
- Parallel download of chunks
- Smaller main bundle size
Vite pre-bundles dependencies for faster cold starts:
- React and React DOM
- Ant Design components
- Common utility libraries
Development server startup:
- CRA: 10-30 seconds
- Vite: 1-2 seconds
Hot Module Replacement:
- CRA: 2-5 seconds
- Vite: < 1 second
Production build:
- CRA: 60-120 seconds
- Vite: 30-60 seconds
Optimized production build includes:
- Tree-shaking for unused code elimination
- Minification with terser
- Asset optimization
- Dynamic imports for route-based code splitting
Problem: Variables are undefined or showing default values.
Solution:
- Ensure variables use
VITE_prefix (notREACT_APP_) - Access via
import.meta.env.VITE_*(notprocess.env) - Restart dev server after changing
.envfiles
// ✅ Correct
const url = import.meta.env.VITE_BACKEND_URL;
// ❌ Wrong
const url = process.env.REACT_APP_BACKEND_URL;Problem: Changes to files don't trigger updates.
Solution:
- Verify
CHOKIDAR_USEPOLLING=truein.env - Check
vite.config.jshaswatch: { usePolling: true } - Ensure Docker volume is properly mounted
Problem: Import errors during build.
Solution:
- Verify the import path is correct
- Check if the module is installed:
bun pm ls <package-name> - Clear node_modules and reinstall:
rm -rf node_modules && bun install - Clear Vite cache:
rm -rf node_modules/.vite
Problem: Cannot start dev server, port 3000 in use.
Solution:
# Find process using port 3000
lsof -ti:3000
# Kill the process
kill -9 $(lsof -ti:3000)
# Or use a different port
vite --port 3001If you experience slow builds or dev server:
- Clear Vite cache:
rm -rf node_modules/.vite - Update dependencies:
bun update - Check for large files in
src/directory - Disable source maps in
vite.config.js(development only)
If you're working with older branches or need to understand the migration:
- See: docs/VITE_MIGRATION.md for comprehensive migration guide
- Migration date: 2025-10-19
- Breaking changes: Environment variables, proxy setup, SVG imports
- Key differences: Build tool, dev server, configuration approach
- Update all
REACT_APP_*toVITE_*in.envfiles - Replace
process.envwithimport.meta.envin code - Update SVG imports to use
?reactquery parameter - Remove
%PUBLIC_URL%from HTML and use absolute paths - Update proxy configuration if using custom setup
- Test HMR and ensure file watching works
- Official Vite Guide
- Vite Configuration Reference
- Environment Variables in Vite
- Vite Build Optimizations
- CLAUDE.md - Project overview and development guidelines
- VITE_MIGRATION.md - Detailed migration documentation
- Ant Design Components - UI component library
- Follow existing code patterns and conventions
- Run
bun run lint:allbefore committing - Ensure all tests pass:
bun test - Use meaningful component and variable names
The project may use pre-commit hooks for:
- Biome linting and formatting
- Test execution
- Create a feature branch from
main - Make your changes with clear, descriptive commits
- Ensure all linting and tests pass
- Update documentation if needed
- Submit PR with detailed description
frontend/
├── docs/ # Documentation files
│ └── VITE_MIGRATION.md # Vite migration guide
├── public/ # Static assets (served as-is)
│ ├── manifest.json
│ └── robots.txt
├── src/ # Source code
│ ├── assets/ # Images, fonts, etc.
│ ├── components/ # Reusable React components
│ ├── helpers/ # Utility functions and helpers
│ ├── pages/ # Page components
│ ├── config.js # App configuration
│ └── index.jsx # Application entry point
├── index.html # HTML entry point (Vite)
├── vite.config.js # Vite configuration
├── biome.json # Biome linter/formatter configuration
├── package.json # Dependencies and scripts
└── .env # Environment variables (VITE_ prefix)
For questions or issues:
- Check VITE_MIGRATION.md for migration-related issues
- Review Vite documentation for build tool questions
- Consult CLAUDE.md for project-specific guidelines
- Open an issue in the project repository with detailed information