npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

create-bini-app

v9.3.2

Published

Official CLI for Bini.js — scaffolds a React + Vite app with file-based routing, API routes, TypeScript or JavaScript, Tailwind CSS or CSS Modules, automatic favicon generation, and PWA support.

Readme

██████╗ ██╗███╗   ██╗██╗      ██╗███████╗
██╔══██╗██║████╗  ██║██║      ██║██╔════╝
██████╔╝██║██╔██╗ ██║██║      ██║███████╗
██╔══██╗██║██║╚██╗██║██║ ██╗  ██║╚════██║
██████╔╝██║██║ ╚████║██║ ╚█████╔╝███████║
╚═════╝ ╚═╝╚═╝  ╚═══╝╚═╝  ╚════╝ ╚══════╝

A React + Vite framework with file-based routing, API routes, and a zero-dependency production server

npm version total downloads license node version

vite react typescript hono


Quick Start

npx create-bini-app@latest my-app
cd my-app
npm install
npm run dev

Opens http://localhost:3000 automatically.


What You Get

Running create-bini-app scaffolds a complete project with:

  • File-based routing via bini-routerpage.tsx files map to URLs, nested layouts, per-route metadata, automatic code splitting
  • API routes powered by Hono — plain function handlers or full Hono apps in src/app/api/
  • Zero-dependency production server via bini-server — pure Node.js http, serves dist/ and proxies /api/*
  • Dev overlay via bini-overlay — animated logo badge and full error overlay with source maps
  • Clean startup banner via bini-env — shows active .env files on dev server start
  • Automatic favicons — SVG, PNG at 512×512, apple-touch-icon, og-image.png, and site.webmanifest generated at scaffold time
  • Official plugin system — extend Bini.js with first-party and community plugins via bini.config
  • TypeScript or JavaScript — your choice at scaffold time
  • Tailwind CSS, CSS Modules, or plain CSS — your choice at scaffold time
  • Netlify Edge Functions — API routes deploy to Netlify Edge by default, zero extra config

CLI Usage

# Interactive — prompts for name and options
npx create-bini-app@latest

# Pass project name directly
npx create-bini-app@latest my-app

# Skip prompts with flags
npx create-bini-app@latest my-app --typescript --tailwind
npx create-bini-app@latest my-app --javascript --css-modules
npx create-bini-app@latest my-app --force   # overwrite existing directory

| Flag | Description | |---|---| | --typescript | Use TypeScript | | --javascript | Use JavaScript | | --tailwind | Use Tailwind CSS | | --css-modules | Use CSS Modules | | --force | Overwrite an existing directory | | --minimal | Scaffold with fewer files | | --version, -v | Print CLI version | | --help, -h | Show help |


Project Structure

my-app/
├── src/
│   ├── app/
│   │   ├── api/            ← API route handlers
│   │   │   └── hello.ts    → /api/hello
│   │   ├── layout.tsx      ← Root layout + global metadata
│   │   ├── page.tsx        ← / (home page)
│   │   ├── not-found.tsx   ← Custom 404 page (optional)
│   │   └── globals.css
│   ├── main.tsx            ← React entry point
│   └── App.tsx             ← Auto-generated by bini-router — do not edit
├── public/                 ← Favicons, og-image, site.webmanifest
├── netlify/
│   └── edge-functions/
│       └── api.ts          ← Auto-generated by bini-router on build — do not edit
├── bini.config.ts
├── vite.config.ts
└── package.json

Scripts

| Command | Description | |---|---| | npm run dev | Start Vite dev server with HMR | | npm run build | Bundle for production into dist/ + generate Netlify edge function | | npm run export | Static SPA export (vite build --mode export) | | npm start | Serve the production build via bini-server | | npm run preview | Preview the production build via Vite | | npm run type-check | TypeScript type check (TS projects only) | | npm run lint | Lint with ESLint |


File-Based Routing

bini-router maps page.tsx files to URLs — pure SPA, no server required at runtime.

src/app/
  page.tsx                 → /
  about.tsx                → /about  (file-based, no folder needed)
  dashboard/
    layout.tsx             → wraps /dashboard and all children
    page.tsx               → /dashboard
    [id]/
      page.tsx             → /dashboard/:id
  blog/
    [slug]/
      page.tsx             → /blog/:slug
  not-found.tsx            → custom 404 (optional)

Pages

export default function Dashboard() {
  return <h1>Dashboard</h1>
}

Dynamic routes

// src/app/blog/[slug]/page.tsx
import { useParams } from 'react-router-dom'

export default function Post() {
  const { slug } = useParams()
  return <h1>Post: {slug}</h1>
}

Layouts

Root layouts use {children} — they wrap from outside the router. Nested layouts use <Outlet /> — they are React Router route wrappers.

// src/app/layout.tsx — root layout
export const metadata = {
  title      : 'My App',
  description: 'Built with Bini.js',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return <>{children}</>
}
// src/app/dashboard/layout.tsx — nested layout
import { Outlet } from 'react-router-dom'

export default function DashboardLayout() {
  return (
    <div>
      <aside>Sidebar</aside>
      <main><Outlet /></main>
    </div>
  )
}

Note: Root layouts that render an <html> tag are automatically skipped by bini-router — they are treated as HTML shell layouts and excluded from the route wrapping chain.

Metadata

Export metadata from any layout.tsx. All fields are optional. Metadata is stripped from the browser bundle at build time — it never ships to the client.

export const metadata = {
  title      : 'Dashboard',
  description: 'Your personal dashboard',
  themeColor : '#00CFFF',
  manifest   : '/site.webmanifest',
  openGraph  : {
    title : 'Dashboard',
    url   : 'https://myapp.com/dashboard',
    images: [{ url: '/og-image.png', width: 1200, height: 630 }],
  },
  twitter: {
    card   : 'summary_large_image',
    creator: '@yourhandle',
    images : ['/og-image.png'],
  },
  icons: {
    icon : [{ url: '/favicon.svg', type: 'image/svg+xml' }],
    apple: [{ url: '/apple-touch-icon.png', sizes: '180x180' }],
  },
}

Per-route titles can also be set from nested layout.tsx files — bini-router reads the title field and injects a <TitleSetter> that updates document.title when the route is active.


API Routes

Create files in src/app/api/. Both plain function handlers and Hono apps are supported. The same files run in dev (Vite middleware), production (bini-server), and on Netlify Edge (auto-generated edge function).

src/app/api/
  hello.ts           → /api/hello
  users.ts           → /api/users
  posts/
    index.ts         → /api/posts
    [id].ts          → /api/posts/:id
  [...catch].ts      → /api/* catch-all

Plain function handler

// src/app/api/hello.ts
export default function handler(req: Request) {
  return Response.json({ message: 'hello', method: req.method })
}

Hono app

// src/app/api/users.ts
import { Hono } from 'hono'

const app = new Hono().basePath('/api')

app.get('/users', (c) => c.json({ users: ['alice', 'bob'] }))

app.post('/users', async (c) => {
  const body = await c.req.json()
  return c.json({ created: body }, 201)
})

export default app

Dynamic API routes

// src/app/api/posts/[id].ts
import { Hono } from 'hono'

const app = new Hono().basePath('/api')

app.get('/posts/:id', (c) => c.json({ id: c.req.param('id') }))

export default app

How API routing works

In dev, bini-router registers a Vite middleware that intercepts /api/* requests, scans your src/app/api/ directory, matches the route, and calls the handler — with hot module replacement on every save.

On build, bini-router auto-generates netlify/edge-functions/api.ts. Hono apps are detected by their import and merged via app.route() — a single shared Hono instance, no duplicate module loading. Plain function handlers are wrapped with app.all().


Plugin System

Bini.js has an official plugin system. Plugins let you extend the framework with reusable behaviour — hook into the dev server, build pipeline, routing, and API layer without touching your app code.

Using plugins

Install a plugin and register it in bini.config.ts:

// bini.config.ts
import { defineConfig } from 'bini-router'
import myPlugin from 'bini-plugin-example'

export default defineConfig({
  plugins: [
    myPlugin({ /* options */ }),
  ],
})

Multiple plugins are supported and run in order:

export default defineConfig({
  plugins: [
    authPlugin(),
    analyticsPlugin({ trackPageViews: true }),
    i18nPlugin({ locales: ['en', 'si'] }),
  ],
})

Writing a plugin

A Bini.js plugin is a function that returns a plugin object:

import type { BiniPlugin } from 'bini-router'

export default function myPlugin(options = {}): BiniPlugin {
  return {
    name: 'bini-plugin-my-plugin',

    onDevStart({ server }) {
      console.log('Dev server ready')
    },

    onBuildStart({ config }) {
      console.log('Building...')
    },

    onBuildEnd({ outDir }) {
      console.log(`Built to ${outDir}`)
    },

    onApiSetup({ app }) {
      app.get('/api/my-plugin/status', (c) => c.json({ ok: true }))
    },
  }
}

All lifecycle hooks are optional — implement only what your plugin needs.

Available hooks

| Hook | When it runs | Arguments | |---|---|---| | onDevStart | Dev server ready | { server, config } | | onBuildStart | Before vite build | { config } | | onBuildEnd | After vite build | { outDir, config } | | onApiSetup | API router initialised | { app: Hono } | | onRequest | Every incoming request | { req, next } |

Official plugins

| Plugin | Description | |---|---| | bini-plugin-auth | Session-based and JWT authentication | | bini-plugin-i18n | Internationalisation and locale routing | | bini-plugin-analytics | Privacy-first page view analytics |

Community plugins follow the bini-plugin-* naming convention. See the plugin registry for the full list.


Production Server

bini-server is a zero-dependency production server — pure Node.js http, no Express, no Fastify.

npm run build   # vite build → dist/
npm start       # bini-server

Terminal output:

  ß Bini.js (production)

  ➜  Environments: .env, .env.production
  ➜  Local:   http://localhost:3000/
  ➜  Network: http://192.168.1.5:3000/

Request flow:

Request
  ├─ /api/*  →  src/app/api/ handlers  (Hono apps or plain functions)
  ├─ /*      →  stream static file from dist/
  └─ /*      →  dist/index.html  (SPA fallback)

| Feature | vite preview | bini-server | |---|---|---| | Serves dist/ | ✅ | ✅ | | API routes | ✅ | ✅ | | SPA fallback | ✅ | ✅ | | Production use | ❌ not recommended | ✅ | | Body timeout | ❌ | ✅ 30s | | Body size limit | ❌ | ✅ 10 MB | | Handler timeout | ❌ | ✅ 30s | | Graceful shutdown | ❌ | ✅ | | Zero dependencies | ✅ | ✅ |

Override the default port via environment variable:

PORT=8080 npm start

Deployment

Netlify (default)

Every Bini.js project is pre-configured for Netlify out of the box. Running vite build automatically generates netlify/edge-functions/api.ts — no extra setup needed.

# netlify.toml
[build]
  command = "vite build"
  publish = "dist"

[[edge_functions]]
  path     = "/api/*"
  function = "api"

[[redirects]]
  from   = "/*"
  to     = "/index.html"
  status = 200

Use [[edge_functions]] for the API — not [[redirects]].

Node.js servers (Railway, Render, Fly.io, VPS)

npm run build
npm start

Railway and Render inject PORT automatically. Use pm2 on a VPS:

npm install -g pm2
pm2 start "npm start" --name my-app
pm2 save && pm2 startup

Cloudflare Workers

npm install wrangler
// vite.config.ts
biniroute({ platform: 'cloudflare' })
vite build && npx wrangler deploy

Vercel

⚠️ Vercel is not yet supported. We are actively working on a Vercel adapter — follow the issue for updates.

Other platforms

bini-router supports netlify · cloudflare · node · deno · bun · aws. Set platform in vite.config.ts once — vite build generates the correct platform entry file automatically.

| Platform | Output file | |---|---| | netlify | netlify/edge-functions/api.ts | | cloudflare | worker.ts / worker.js | | node | server/index.ts / server/index.js | | deno | server/index.ts / server/index.js | | bun | server/index.ts / server/index.js | | aws | handler.ts / handler.js |


Environment Variables

bini-env detects and displays active .env files on every server start (dev and preview):

  ß Bini.js (dev)

  ➜  Environments: .env.local, .env
  ➜  Local:   http://localhost:3000/
  ➜  Network: http://192.168.1.10:3000/

Files are checked in priority order: .env.local.env.[mode].local.env.[mode].env

Prefix variables with VITE_ to expose them to the browser:

# .env
VITE_API_URL=https://api.example.com

# .env.local (overrides .env, git-ignored)
VITE_API_URL=http://localhost:4000

Dev Overlay

bini-overlay adds an animated Bini.js logo badge to the bottom-left corner during development. It has three states:

| State | Behaviour | |---|---| | Loading | Logo draws itself with a stroke animation and pulses | | Idle | Logo sits quietly as a filled gradient icon | | Error | Badge hides — full error overlay with source maps takes over |

The badge recovers automatically when an error is fixed and HMR fires. Never appears in production builds.

# Disable without touching config
DISABLE_BINI_OVERLAY=true npm run dev

Powered By

| Package | Role | |---|---| | bini-router | File-based routing, nested layouts, metadata, Hono API routes, multi-platform deployment | | bini-server | Zero-dependency production server | | bini-overlay | Dev animated logo badge + error overlay | | bini-env | Clean dev/preview startup banner | | bini-export | Static SPA export for GitHub Pages, Netlify static, S3 | | hono | API route handler runtime | | vite | Dev server and production bundler | | react | UI library | | react-router-dom | Client-side routing |


Resources

  • Website: https://bini.js.org
  • GitHub: https://github.com/Binidu01/bini-cli
  • npm: https://www.npmjs.com/package/create-bini-app
  • Issues: https://github.com/Binidu01/bini-cli/issues

MIT © Binidu Ranasinghe