on GitHub

Start as a script. Ship to your real app.

It starts as the one Python script you'd already write. As it grows, n6k splits the compute out into a cached data.py and lets the view become Markdown or plain React. So unlike Streamlit, Dash, or Panel, you never hit the wall where the app is the render loop, with nowhere to add auth or your own frontend.

uvx n6k init
uv run n6k dev
page.py
start here

Your whole app in one Python file — the script you'd write anyway. It recomputes on every interaction.

@page
def revenue(c: Components) -> None:
    df = pd.read_parquet("orders.parquet")   # re-read every rerun
    by_month = df.groupby("month", as_index=False).revenue.sum()
    c.Static.BarChart(rows=by_month.to_dict("records"), x="month", y="revenue")
As it grows, move the data work into a data.py — now it's fast
data.py
your data layer

Define the data once in Python. n6k caches it and runs the SQL live in the browser.

@db.data()
def orders() -> pd.DataFrame:
    return pd.read_parquet("orders.parquet")

@orders.view()
def by_month() -> str:
    return "SELECT month, sum(revenue) AS revenue FROM orders GROUP BY month"

The same chart over that one data.py — your choice of syntax.

<BarChart data="db.by_month" x="month" y="revenue" />
Grows into a real app
n6k.py
your backend

Add auth — it's plain FastAPI middleware over your own app.

from n6k_app import create_app

app = create_app()

@app.middleware("http")
async def auth(request, call_next):
    # your auth check here
    return await call_next(request)
app/revenue/page.tsx
your frontend

Or drop the same chart straight into your Next.js app.

"use client";
import { QueryProvider, DuckDBProvider } from "@n6k.io/db/react";
import { BarChart } from "@n6k.io/ui/components";

export default function Page() {
  return (
    <QueryProvider>
      <DuckDBProvider databases={{ db: "n6k://your-server/p/revenue" }}>
        <BarChart data="db.by_month" x="month" y="revenue" />
      </DuckDBProvider>
    </QueryProvider>
  );
}

Computed in your browser

This page's entire backend, and the three figures it answers. Each one is a SQL query that ran in your browser when the page loaded.

Loading source…

Rows
SELECT (count(*)) FROM db.iris
Avg sepal length
SELECT (avg(sepal_length)) FROM db.iris
Species
SELECT (count(distinct species)) FROM db.iris

A database you can attach

That table is a real DuckDB catalog, reachable from anything that speaks DuckDB. It's attached here as db. Type a query.

The same console is in the toolbar of every page on this site.

What people build with it

Finished apps on real data. Every page is source-viewable: flip the Display / Frontend / Backend tabs at the top of any page.

All showcases →

Everything it can do

One entry per feature. Jump straight to the one you're evaluating.

All capabilities →
Build your first page → Tutorial