Meta-package and installer for the complete nb_ frontend stack
NbStack provides a single command to install and configure a modern Phoenix frontend development experience with Vite, Inertia.js, type-safe routing, and TypeScript integration.
NbStack orchestrates the installation of:
- nb_vite - Vite integration for Phoenix with HMR and optimized builds
- nb_inertia - Inertia.js integration with declarative page DSL and modal support
- nb_routes - Type-safe route helpers with rich mode and form helpers
- nb_ts - TypeScript type generation from Elixir serializers and Inertia pages
- nb_serializer - High-performance JSON serialization with declarative DSL
Run the installer directly from GitHub:
mix igniter.install nb_stack@github:nordbeam/nb_stackThis single command installs and configures the entire stack with smart defaults optimized for modern Phoenix development.
The installer:
- ✅ Installs nb_vite with TypeScript support and Vite dev server
- ✅ Generates type-safe route helpers with rich mode (returns
{ url, method }objects) - ✅ Configures nb_serializer for high-performance JSON with camelized props
- ✅ Sets up TypeScript type generation for props and serializers
- ✅ Installs nb_inertia with your chosen framework and enhanced components
- ✅ Adds Vite nbRoutes plugin for auto-regenerating routes on file save
- ✅ Coordinates configuration across all packages (camelize_props, TypeScript paths, etc.)
By default, NbStack installs:
- React + TypeScript
- Rich routes with method variants and form helpers
- Serializer with automatic type generation
- Camelized props for JavaScript conventions
- Enhanced Inertia components with nb_routes integration
Customize the installation:
# Install with defaults (React + TypeScript)
mix igniter.install nb_stack
# Install with Vue
mix igniter.install nb_stack --framework vue
# Install with Svelte
mix igniter.install nb_stack --framework svelte
# Install without TypeScript
mix igniter.install nb_stack --no-typescript
# Install with SSR
mix igniter.install nb_stack --ssr
# Skip confirmation prompts
mix igniter.install nb_stack --yes--framework- Client framework:react(default),vue, orsvelte--typescript- Enable TypeScript (default:true)--ssr- Enable server-side rendering (default:false)--yes- Skip confirmation prompts
After installation, create your first Inertia page:
# lib/my_app_web/controllers/page_controller.ex
defmodule MyAppWeb.PageController do
use MyAppWeb, :controller
use NbInertia.Controller
inertia_page :home do
prop :greeting, type: ~TS"string"
end
def home(conn, _params) do
render_inertia(conn, :home,
greeting: "Hello from NbStack!"
)
end
end# lib/my_app_web/router.ex
scope "/", MyAppWeb do
pipe_through :browser
get "/", PageController, :home
end// assets/js/pages/Home.tsx
import React from 'react';
import { Link } from '@/lib/inertia';
import { users_path } from '@/routes';
import type { HomeProps } from '@/types';
export default function Home({ greeting }: HomeProps) {
return (
<div>
<h1>{greeting}</h1>
<Link href={users_path()}>View Users</Link>
</div>
);
}mix phx.serverVisit http://localhost:4000 to see your Inertia page!
NbStack provides a cohesive development experience where all packages work together seamlessly.
Routes return RouteResult objects containing both URL and HTTP method:
import { user_path, update_user_path } from '@/routes';
// Returns: { url: "/users/1", method: "get" }
user_path(1);
// Returns: { url: "/users/1", method: "patch" }
update_user_path.patch(1);Import enhanced components from @/lib/inertia (created by installer):
import { router, Link, useForm } from '@/lib/inertia';
import { user_path, update_user_path } from '@/routes';
// Navigation - method automatically detected
router.visit(user_path(1));
// Links - method from RouteResult
<Link href={user_path(1)}>View User</Link>
// Forms - bound to route
const form = useForm(data, update_user_path.patch(1));
form.submit(); // No method or URL needed!Define serializers in Elixir, get TypeScript types automatically:
# lib/my_app/users/user_serializer.ex
defmodule MyApp.Users.UserSerializer do
use NbSerializer.Serializer
field :id, :integer
field :name, :string
field :email, :string
endAfter running mix ts.gen:
// Generated in assets/js/types/index.ts
export interface User {
id: number;
name: string;
email: string;
}Use in your Inertia pages with full type safety:
inertia_page :users_index do
prop :users, {:array, UserSerializer}
end// Type automatically generated
export interface UsersIndexProps {
users: User[];
}The Vite plugin watches your Phoenix router and automatically regenerates route helpers on changes:
- Add a route to
router.ex - Save the file
- Routes regenerate instantly
- HMR updates your browser
- New route immediately available in frontend
No manual mix nb_routes.gen needed during development!
Here's how the full stack provides end-to-end type safety:
┌──────────────────────────────────────────────────────────┐
│ Phoenix Backend │
│ │
│ 1. Define routes in router.ex │
│ 2. Create serializers with NbSerializer │
│ 3. Define Inertia pages with typed props │
│ │
│ inertia_page :users_index do │
│ prop :users, {:array, UserSerializer} │
│ end │
└────────────────────┬─────────────────────────────────────┘
│
│ Compile-time Code Generation
▼
┌──────────────────────────────────────────────────────────┐
│ Frontend (Assets) │
│ │
│ • routes.js + routes.d.ts (from nb_routes) │
│ • types/index.ts (from nb_ts) │
│ • lib/inertia.ts (enhanced components) │
│ │
│ import { user_path } from '@/routes'; │
│ import { Link } from '@/lib/inertia'; │
│ import type { User, UsersIndexProps } from '@/types'; │
│ │
│ export default function Index({ users }: UsersIndexProps)│
│ return users.map(user => ( │
│ <Link href={user_path(user.id)}>{user.name}</Link> │
│ )); │
│ } │
└──────────────────────────────────────────────────────────┘
After installation, your project will have:
my_app/
├── assets/
│ ├── js/
│ │ ├── app.tsx # Inertia entry point
│ │ ├── routes.js # Generated route helpers
│ │ ├── routes.d.ts # TypeScript definitions for routes
│ │ ├── lib/
│ │ │ └── inertia.ts # Enhanced Inertia components
│ │ ├── pages/
│ │ │ └── Home.tsx # Sample page component
│ │ └── types/
│ │ └── index.ts # Generated TypeScript types
│ ├── vite.config.ts # Vite configuration with nbRoutes plugin
│ ├── tsconfig.json # TypeScript configuration
│ └── package.json # npm dependencies
├── config/
│ └── config.exs # Coordinated nb_ configuration
└── lib/
└── my_app_web/
├── controllers/ # Your controllers with inertia_page
└── router.ex # Phoenix routes
- NbStack: You're reading it!
- nb_vite: https://hexdocs.pm/nb_vite
- nb_inertia: https://hexdocs.pm/nb_inertia
- nb_routes: https://hexdocs.pm/nb_routes
- nb_ts: https://hexdocs.pm/nb_ts
- nb_serializer: https://hexdocs.pm/nb_serializer
- Inertia.js: https://inertiajs.com
NbStack follows these design principles:
- Convention over Configuration - Smart defaults that work together
- Coordinated Setup - All packages configured consistently
- Type Safety - End-to-end types from Elixir to TypeScript
- Developer Experience - Fast feedback loops with HMR and auto-regeneration
- Optional Dependencies - Each package can be used standalone if needed
mix igniter.install nb_vite --typescript
mix nb_routes.gen --variant rich --with-methods --with-forms
mix nb_serializer.install --with-typescript --camelize-props
mix nb_ts.install --output-dir assets/js/types
mix nb_inertia.install --client-framework react --typescript --camelize-props
# Then manually:
# - Add nbRoutes plugin to vite.config.ts
# - Configure camelize_props in both nb_inertia and nb_serializer
# - Set up TypeScript paths
# - Create lib/inertia.tsmix igniter.install nb_stackThat's it! Everything is installed, configured, and coordinated automatically.
If TypeScript types aren't generating, run:
mix compile --force
mix ts.genEnsure the nbRoutes plugin is in your vite.config.ts:
import { nbRoutes } from '@nordbeam/nb-vite/nb-routes';
export default defineConfig({
plugins: [
phoenix({ input: ['js/app.tsx'] }),
nbRoutes({ enabled: true })
]
});Check that both nb_inertia and nb_serializer have camelize_props: true:
# config/config.exs
config :nb_inertia, camelize_props: true
config :nb_serializer, camelize_props: trueNbStack is part of the nb_ monorepo. Contributions welcome!
MIT License. See LICENSE for details.
Built by Nordbeam to make Phoenix frontend development delightful.