Vitex

[![Hex.pm](https://img.shields.io/hexpm/v/vitex.svg)](https://hex.pm/packages/vitex) [![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/vitex/) [![License](https://img.shields.io/hexpm/l/vitex.svg)](https://github.com/nordbeam/vitex/blob/main/LICENSE) [![Elixir Version](https://img.shields.io/badge/elixir-~%3E%201.14-purple)](https://elixir-lang.org/) [![Phoenix Version](https://img.shields.io/badge/phoenix-~%3E%201.8-orange)](https://www.phoenixframework.org/) **Phoenix integration for Vite - a fast frontend build tool** [Features](#features) • [Installation](#installation) • [Usage](#usage) • [Configuration](#configuration) • [Documentation](#documentation)

Table of Contents

Introduction

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.

Why Vitex?

  • ⚡ 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

Features

  • 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

Installation

The easiest way to add Vitex to your Phoenix application is using the automatic installer with Igniter:

  1. Use the Igniter installer.
mix archive.install hex igniter_new
  1. 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 all features
mix igniter.install vitex --react --typescript --tls

The installer will:

  • Create vite.config.js with appropriate settings
  • Update package.json with necessary dependencies
  • Configure Phoenix watchers for development
  • Update your root layout to use Vite helpers
  • Set up asset files for your chosen configuration

Manual Installation

If you prefer manual setup or don't want to use Igniter:

  1. Add Vitex to your dependencies:
# mix.exs
def deps do
  [
    {:vitex, "~> 0.1.0"}
  ]
end
  1. 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
    })
  ],
})
  1. Update assets/package.json:
{
  "name": "your_app",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "vite": "^7.0.0"
  }
}
  1. Update your Phoenix configuration:
# config/dev.exs
config :your_app, YourAppWeb.Endpoint,
  watchers: [
    node: ["node_modules/.bin/vite", cd: "assets"]
  ]
  1. 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>

Usage

Basic Usage

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.server

Vite will automatically start in development mode with HMR enabled.

React Support

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
    })
  ],
})

TypeScript Support

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/**/*"]
}

Inertia.js Integration

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>
  )
}

Server-Side Rendering (SSR)

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.build

Configuration

Vite Configuration

The 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
})

TLS/HTTPS Setup

Vitex can automatically detect local TLS certificates. Enable with:

phoenix({
  detectTls: true,
  // ... other options
})

For manual TLS configuration, see the TLS setup guide.

Environment Variables

Vitex respects the following environment variables:

  • NODE_ENV - Set to "production" for production builds
  • VITE_PORT - Custom Vite dev server port
  • VITE_DEV_SERVER_KEY - Path to TLS key file
  • VITE_DEV_SERVER_CERT - Path to TLS certificate file

Mix Tasks

Vitex provides several Mix tasks:

mix vitex

Run Vite commands directly:

mix vitex build         # Build for production
mix vitex dev          # Start dev server
mix vitex preview      # Preview production build

mix vitex.install

Install and configure Vitex (requires Igniter):

mix vitex.install [options]

Options:
  --react        Enable React support
  --typescript   Enable TypeScript
  --inertia      Enable Inertia.js (includes React)
  --tls          Enable TLS auto-detection
  --ssr          Enable SSR support

mix vitex.build

Build assets for production:

mix vitex.build

mix vitex.ssr.build

Build SSR bundle:

mix vitex.ssr.build

Helper Functions

Vitex provides the following helper functions:

Vitex.vite_assets/1

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 entries

Vitex.vite_client/0

Enable HMR in development:

Vitex.vite_client()
# In dev: <script type="module" src="http://localhost:5173/@vite/client"></script>
# In prod: <!-- nothing -->

Vitex.react_refresh/0

Enable React Fast Refresh:

Vitex.react_refresh()
# Injects React Refresh runtime in development

Vitex.asset_path/1

Get 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"

Vitex.hmr_enabled?/0

Check if HMR is active:

if Vitex.hmr_enabled?() do
  # Development-specific code
end

Common Use Cases

Single Page Applications

Build 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']
        }
      }
    }
  }
})

Tailwind CSS

Vitex works great with Tailwind CSS v4:

// vite.config.js
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    tailwindcss(),
    phoenix({
      // ... options
    })
  ],
})

Multiple Entry Points

Support multiple sections of your app:

phoenix({
  input: [
    'js/app.js',
    'js/admin.js',
    'css/app.css',
    'css/admin.css'
  ],
  // ... other options
})

Code Splitting

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()
}

Troubleshooting

Common Issues

Vite dev server not starting

  • Check that Node.js is installed (v18+ recommended)
  • Ensure assets/package.json exists
  • Run npm install in the assets directory

Assets not loading in production

  • Run mix vitex.build before deploying
  • Check that manifest.json is generated in priv/static/assets/
  • Ensure priv/static is included in your release

HMR not working

  • Verify Vite dev server is running (check priv/hot file)
  • 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 --noEmit for full type checking

Getting Help

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# 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 build

License

This project is licensed under the MIT License - see the LICENSE file for details.

Copyright (c) 2025 Nordbeam Team


Made with ❤️ by the Nordbeam Team