Phoenix integration for Vite - a fast frontend build tool
Features β’ Installation β’ Usage β’ Configuration β’ Documentation
- Introduction
- Features
- Installation
- Usage
- Configuration
- Mix Tasks
- Helper Functions
- Common Use Cases
- Troubleshooting
- Contributing
- License
Vitex brings the power of Vite to Phoenix applications, replacing the traditional esbuild setup with a modern, fast, and feature-rich development experience. With Vitex, you get instant hot module replacement (HMR), optimized production builds, and seamless integration with modern frontend frameworks.
- β‘ Lightning Fast HMR: See your changes instantly without page reloads
- π§ Zero Configuration: Works out of the box with sensible defaults
- π― Framework Agnostic: Support for React, Vue, Svelte, and vanilla JavaScript
- π¦ Optimized Builds: Automatic code splitting and tree shaking
- π₯ Modern Development: ES modules, TypeScript, JSX, and CSS modules support
- π Production Ready: Efficient bundling with rollup under the hood
- β Hot Module Replacement (HMR) - Instant updates without losing state
- β React Fast Refresh - Preserve component state during development
- β TypeScript Support - First-class TypeScript support with zero config
- β Inertia.js Integration - Build SPAs with server-side routing
- β SSR Support - Server-side rendering for better SEO and performance
- β Asset Optimization - Automatic minification, tree-shaking, and code splitting
- β CSS Processing - PostCSS, CSS modules, and preprocessor support
- β Static Asset Handling - Import images, fonts, and other assets
- β Manifest Generation - Production-ready asset manifests with hashing
- β Multiple Entry Points - Support for multiple JavaScript/CSS entry files
- β Phoenix LiveView Compatible - Works seamlessly with LiveView
- β Automatic TLS Detection - Detects and uses local certificates for HTTPS
The easiest way to add Vitex to your Phoenix application is using the automatic installer with Igniter:
- Use the Igniter installer.
mix archive.install hex igniter_new- Run the installer:
# Basic installation
mix igniter.install vitex
# With React support
mix igniter.install vitex --react
# With TypeScript
mix igniter.install vitex --typescript
# With Inertia.js (includes React)
mix igniter.install vitex --inertia
# With shadcn/ui components (requires TypeScript and React/Inertia)
mix igniter.install vitex --typescript --react --shadcn
# With custom shadcn theme color
mix igniter.install vitex --typescript --react --shadcn --base-color slate
# With Bun as package manager (Elixir-managed)
mix igniter.install vitex --bun
# With all features
mix igniter.install vitex --react --typescript --tls --bun--react- Enable React with Fast Refresh support--typescript- Enable TypeScript support--inertia- Enable Inertia.js for building SPAs (automatically includes React)--shadcn- Enable shadcn/ui component library (requires TypeScript and React/Inertia)--base-color- Set shadcn/ui theme color: neutral (default), gray, zinc, stone, or slate--bun- Use Bun as the package manager via the Elixir bun package--tls- Enable automatic TLS certificate detection for HTTPS development--ssr- Enable Server-Side Rendering support
The installer will:
- Create
vite.config.jswith appropriate settings - Update
package.jsonwith necessary dependencies - Configure Phoenix watchers for development
- Update your root layout to use Vite helpers
- Set up asset files for your chosen configuration
If you prefer manual setup or don't want to use Igniter:
- Add Vitex to your dependencies:
# mix.exs
def deps do
[
{:vitex, "~> 0.2"}
]
end- Create
assets/vite.config.js:
import { defineConfig } from 'vite'
import phoenix from '../deps/vitex/priv/static/vitex'
export default defineConfig({
plugins: [
phoenix({
input: ['js/app.js', 'css/app.css'],
publicDirectory: '../priv/static',
buildDirectory: 'assets',
hotFile: '../priv/hot',
manifestPath: '../priv/static/assets/manifest.json',
refresh: true
})
],
})- Update
assets/package.json:
{
"name": "your_app",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"vite": "^7.0.0"
}
}- Update your Phoenix configuration:
# config/dev.exs
config :your_app, YourAppWeb.Endpoint,
watchers: [
node: ["node_modules/.bin/vite", cd: "assets"]
]- Update your root layout:
# lib/your_app_web/components/layouts/root.html.heex
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
<%= Vitex.vite_client() %>
<%= Vitex.vite_assets("css/app.css") %>
<%= Vitex.vite_assets("js/app.js") %>
</head>
<!-- ... -->
</html>After installation, Vitex provides helper functions for your templates:
<!-- In your root layout -->
<%= Vitex.vite_client() %> <!-- Enables HMR in development -->
<%= Vitex.vite_assets("js/app.js") %>
<%= Vitex.vite_assets("css/app.css") %>Start your Phoenix server:
mix phx.serverVite will automatically start in development mode with HMR enabled.
To use React with Fast Refresh:
<!-- In your layout -->
<%= Vitex.react_refresh() %> <!-- Add before your app scripts -->
<%= Vitex.vite_assets("js/app.jsx") %>Configure Vite for React:
// vite.config.js
import { defineConfig } from 'vite'
import phoenix from '../deps/vitex/priv/static/vitex'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react(),
phoenix({
input: ['js/app.jsx', 'css/app.css'],
reactRefresh: true,
// ... other options
})
],
})Vitex supports TypeScript out of the box:
// vite.config.js
export default defineConfig({
plugins: [
phoenix({
input: ['js/app.ts', 'css/app.css'],
// ... other options
})
],
})Create assets/tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"jsx": "react-jsx",
"strict": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["js/**/*"]
}For building SPAs with Inertia.js:
# In your controller
def index(conn, _params) do
conn
|> assign_prop(:users, Users.list_users())
|> render_inertia("Users/Index")
end// assets/js/pages/Users/Index.jsx
import React from 'react'
export default function UsersIndex({ users }) {
return (
<div>
<h1>Users</h1>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
)
}Vitex supports shadcn/ui, a collection of reusable components built with Radix UI and Tailwind CSS.
Requirements:
- TypeScript must be enabled (
--typescript) - Either React (
--react) or Inertia.js (--inertia) must be enabled
# Install with shadcn/ui
mix igniter.install vitex --typescript --react --shadcn
# With custom theme color (neutral, gray, zinc, stone, slate)
mix igniter.install vitex --typescript --react --shadcn --base-color slateThe installer will:
- Configure path aliases for component imports
- Initialize shadcn/ui with your chosen theme
- Set up CSS variables for theming
- Create the components directory structure
Adding Components:
cd assets && npx shadcn@latest add button
cd assets && npx shadcn@latest add card dialogUsage in your React components:
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
export default function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<Button variant="outline">Click me</Button>
</CardContent>
</Card>
)
}Path Aliases:
@- Root JavaScript directory (assets/js)@/components- Component directory@/lib- Utility functions@/hooks- Custom React hooks
Enable SSR in your Vite config:
// vite.config.js
export default defineConfig({
plugins: [
phoenix({
input: ['js/app.js', 'css/app.css'],
ssr: 'js/ssr.js',
// ... other options
})
],
})Build your SSR bundle:
mix vitex.ssr.buildThe Phoenix Vite plugin accepts the following options:
phoenix({
// Entry files (required)
input: ['js/app.js', 'css/app.css'],
// Output directories
publicDirectory: '../priv/static',
buildDirectory: 'assets',
// Development server
hotFile: '../priv/hot',
detectTls: true, // Auto-detect local certificates
// Build options
manifestPath: '../priv/static/assets/manifest.json',
// Features
refresh: true, // Enable full page reload on blade/heex changes
reactRefresh: true, // Enable React Fast Refresh
// SSR
ssr: 'js/ssr.js', // SSR entry point
})Vitex can automatically detect local TLS certificates. Enable with:
phoenix({
detectTls: true,
// ... other options
})For manual TLS configuration, see the TLS setup guide.
Vitex respects the following environment variables:
NODE_ENV- Set to "production" for production buildsVITE_PORT- Custom Vite dev server portVITE_DEV_SERVER_KEY- Path to TLS key fileVITE_DEV_SERVER_CERT- Path to TLS certificate file
Vitex supports two approaches for package management:
By default, Vitex detects and uses whatever package manager is installed on your system (npm, pnpm, yarn, or bun). The installer will:
- Detect your system package manager automatically
- Configure watchers to use
node_modules/.bin/vite - Run
npm install(or equivalent) during setup
When you use the --bun flag, Vitex integrates with the Elixir bun package:
- Adds
{:bun, "~> 1.5", runtime: Mix.env() == :dev}to your dependencies - Downloads and manages the bun executable at
_build/bun - Uses Bun workspaces for Phoenix JS dependencies
- Configures watchers to use
{Bun, :install_and_run, [:dev, []]} - Mix tasks handle the bun installation lifecycle
Example with Bun:
# Install with Bun support
mix igniter.install vitex --bun
# After installation, these commands are available:
mix bun.install # Install bun executable
mix bun assets # Install npm dependencies
mix bun build # Build assets for productionVitex provides several Mix tasks:
Run Vite commands directly:
mix vitex build # Build for production
mix vitex dev # Start dev server
mix vitex preview # Preview production buildInstall and configure Vitex (requires Igniter):
mix vitex.install [options]
Options:
--react Enable React support
--typescript Enable TypeScript
--inertia Enable Inertia.js (includes React)
--shadcn Enable shadcn/ui components (requires TypeScript + React/Inertia)
--base-color Base color for shadcn/ui theme (neutral, gray, zinc, stone, slate)
--tls Enable TLS auto-detection
--ssr Enable SSR supportBuild assets for production:
mix vitex.buildBuild SSR bundle:
mix vitex.ssr.buildVitex provides the following helper functions:
Generate script/link tags for entries:
Vitex.vite_assets("js/app.js")
# In dev: <script type="module" src="http://localhost:5173/js/app.js"></script>
# In prod: <script type="module" src="/assets/app.123abc.js"></script>
Vitex.vite_assets(["js/app.js", "js/admin.js"])
# Generates tags for multiple entriesEnable HMR in development:
Vitex.vite_client()
# In dev: <script type="module" src="http://localhost:5173/@vite/client"></script>
# In prod: <!-- nothing -->Enable React Fast Refresh:
Vitex.react_refresh()
# Injects React Refresh runtime in developmentGet the URL for an asset:
Vitex.asset_path("images/logo.png")
# In dev: "http://localhost:5173/images/logo.png"
# In prod: "/assets/logo.123abc.png"Check if HMR is active:
if Vitex.hmr_enabled?() do
# Development-specific code
endBuild SPAs with client-side routing:
// vite.config.js
export default defineConfig({
plugins: [
phoenix({
input: ['js/app.jsx'],
// ... other options
})
],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom', 'react-router-dom']
}
}
}
}
})Vitex works great with Tailwind CSS v4:
// vite.config.js
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
phoenix({
// ... options
})
],
})Support multiple sections of your app:
phoenix({
input: [
'js/app.js',
'js/admin.js',
'css/app.css',
'css/admin.css'
],
// ... other options
})Vite automatically handles code splitting for dynamic imports:
// Lazy load a component
const AdminPanel = lazy(() => import('./components/AdminPanel'))
// Dynamic import based on route
if (route === '/admin') {
const { initAdmin } = await import('./admin')
initAdmin()
}Vite dev server not starting
- Check that Node.js is installed (v18+ recommended)
- Ensure
assets/package.jsonexists - Run
npm installin the assets directory
Assets not loading in production
- Run
mix vitex.buildbefore deploying - Check that manifest.json is generated in
priv/static/assets/ - Ensure
priv/staticis included in your release
HMR not working
- Verify Vite dev server is running (check
priv/hotfile) - Check browser console for connection errors
- Ensure
Vitex.vite_client()is included in your layout
TypeScript errors
- Vite doesn't type-check by default (for speed)
- Use your editor's TypeScript integration
- Run
tsc --noEmitfor full type checking
- π Documentation
- π¬ Phoenix Forum
- π Issue Tracker
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Clone the repo
git clone https://github.com/nordbeam/vitex.git
cd vitex
# Install dependencies
mix deps.get
cd priv/vitex && npm install
# Run tests
mix test
# Build the Vite plugin
cd priv/vitex && npm run buildThis project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2025 Nordbeam Team