Self-hosted service
caioricciuti/duck-ui avatar
caioricciuti/duck-ui

Duck-UI is a DuckDB workbench that runs entirely in the browser tab

The fully open-source DuckDB workbench that runs in your browser. SQL editor, notebooks, charts, AI assistant. No install, no signup, no backend — your data never leaves the tab.

645 stars43 forksTypeScriptMIT

At a glance

What is it?
Duck-UI is an MIT licensed front end for DuckDB that runs on DuckDB WASM in the browser, with a SQL editor, notebooks, charts, shareable URLs and an AI assistant that can stay local via WebLLM. It suits teams that want to query files without a server, or analysts who need to hand someone a runnable link.
Who is it for?
Adopt Duck-UI when you want to open a Parquet or CSV file and query it without installing anything or touching a server, when you need to hand a colleague a runnable link, or when you need an AI assistant that can stay inside the browser. Do not adopt it as a replacement for a server side analytics stack, since the data lives in the tab and the browser is the limit, and do not assume the official duckdb -ui feature set is covered.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The whole database lives in the tab, and that is the design

Duck-UI is described as the fully open-source DuckDB workbench that runs in your browser, with no install, no signup and no backend, and the claim that your data never leaves the tab. That is not a marketing simplification, it is the architecture. The engine is DuckDB compiled to WebAssembly, loaded as the @duckdb/duckdb-wasm package, so the queries run on your machine against files your browser can reach. The connections list is where this becomes concrete: in-memory WASM, persistent OPFS storage, and external DuckDB http server instances, with DuckLake catalogs attachable through the embedded database manifest, ?load=ducklake: links or plain ATTACH SQL. The data sources you can import are equally local by default, CSV, JSON, Parquet, Arrow, XLSX and .duckdb files, dropped onto the explorer, pasted as a URL, or read straight from S3, GCS, Azure, R2 or MinIO, with local folders through the File System Access API. The audience is analysts who want to interrogate a file they just downloaded, and teams who want to publish an interactive result without standing up infrastructure.

A query editor built on CodeMirror 6 and Svelte 5

The stack is stated plainly: the app is built on Svelte 5 and CodeMirror 6, and the startup bundle is about 350 kB of JavaScript, with everything else, including the AI runtime, loading the first time you use it. The editor features follow from that choice. Completion comes from your catalog and from DuckDB itself, there is DuckDB dialect formatting, and there is an EXPLAIN ANALYZE viewer. The run bindings are specific, Command Enter runs the statement under the cursor and Command Shift Enter runs the whole tab, and a failed query is underlined where DuckDB points at the problem. The results grid is virtualized and carries sort, filter, search, cell selection and copy, column statistics, and exports to CSV, JSON, XLSX and Parquet. One detail there is a genuine design decision rather than a feature list item. A result cut at the row limit is sorted and filtered in DuckDB over the whole answer, not over the rows on screen, so the filter you see is the filter that produced the numbers. Everything else in the interface, history with duration and row count, compare results by diffing a pinned result against a later run, and query parameters through $name placeholders, follows the same principle of keeping the database authoritative.

Notebooks, dashboards and charts without a server

The workspace model treats queries, notebooks and dashboards as tabs, with connections, extensions, settings, saved queries, history and the dashboards list as pages with their own sidebar, and Command K searching across all of it plus tables and recent queries. Notebooks mix SQL, Python through Pyodide with sql() returning pandas DataFrames, and markdown cells, each with its own result and chart. Dashboards are markdown reports with named SQL blocks, charts, tables, inputs and auto refresh, and they are shareable as a link. The chart set is bar, grouped, stacked, line, area, stacked area, pie, donut and scatter, with per series settings, aggregation and PNG export, and charts follow the theme. There is a map path too, since GEOMETRY results render on a map using maplibre-gl, and DuckDB extensions can be installed and loaded from the Data page. The alternative to all of this is a hosted notebook service with a backend, a session model and a bill. Duck-UI takes the opposite position, which suits a solo analyst and an internal tool, and makes collaborative work the harder case, though live sessions address that by inviting another browser into your workspace over WebRTC with shared editors, peer cursors and shared tables, with no server involved.

The AI assistant can stay in the browser or talk to a provider

Duck Brain, the AI feature, is text to SQL plus one click error fixing, and the provider choice is yours. WebLLM runs fully in the browser with no API key and works offline, or you can use OpenAI, Anthropic, or any OpenAI compatible endpoint such as Ollama or DeepSeek. The default privacy behaviour is worth reading carefully. By default only your schema is sent, never your data, and the optional Explain results action sends a small row sample and asks for consent every time. With WebLLM nothing leaves the browser at all. This is a more considered design than the usual pattern of a chat box wired to an API, because the failure mode people worry about with schema-aware assistants is precisely that a query assistant ends up sending table contents to a model provider. The comparison table in the project documentation puts this in context, noting that the official DuckDB UI offers an AI assistant tied to a MotherDuck account, whereas Duck-UI lets you bring your own provider or run the model locally. The other side of the trade is that a local model is a local model, and the size and quality of the model you can run in a browser is a separate constraint from the privacy property.

Sharing an analysis without a backend

The share and open features are the part of the project that most clearly depends on there being no server. A whole analysis, meaning the query, the notebook and the chart configuration, is encoded into a URL, with no server involved, and runnable queries can be embedded in any page with an iframe. The Open in Duck-UI mechanism adds a ?load= parameter to the app URL, optionally with &sql=, and it works for Parquet, CSV, JSON, .duckdb files and ducklake: catalogs. The example given loads a Parquet file and runs a grouped count. The important detail is on the user side rather than the technical side. Openers see exactly what will load and what will run, confirm once, and get a live editor over your own data, and nothing touches a server. That last point is the security boundary of the whole feature, because the file is fetched by the browser directly, which means the host serving the data must allow it. The documentation points at a hosting your data guide for a free R2 or GitHub Pages setup, and the requirement is that the data host needs CORS enabled. In practice that is the one thing to get right, and it is also the reason this feature will silently fail for some users who paste a link to a file on a host that does not send the right headers.

Running it yourself, and what the container does not serve

There are three ways to run it, and the simplest is a container:

bash
docker run -p 5522:5522 ghcr.io/caioricciuti/duck-ui:latest

After that you open http://localhost:5522, which the documentation describes as the whole setup. The compose file offers the same image with a restart policy, a configurable port through DUCK_UI_PORT defaulting to 5522, and environment variables for an optional external DuckDB connection including DUCK_UI_EXTERNAL_HOST, DUCK_UI_EXTERNAL_PORT, DUCK_UI_EXTERNAL_USER and DUCK_UI_EXTERNAL_PASS, plus DUCK_UI_ALLOW_UNSIGNED_EXTENSIONS for DuckDB extension loading. The Dockerfile is worth reading before you expose it. It builds on oven/bun:1-alpine, copies only dist/ into the runtime stage so the server, its dependencies and the config files stay out of reach over HTTP, pins the static file server with bun add --exact [email protected] rather than pulling whatever is latest on build day, creates an unprivileged user with uid 1001, and passes the configuration through an injection script before starting the server. There is also a local launcher that serves the built app on 127.0.0.1 with your files already queued, using the same load links:

bash
bun run build                                   # once; the CLI serves dist/
node cli/duck-ui.js sales.csv events.parquet    # or: npx duck-ui ... once published
node cli/duck-ui.js db.duckdb --port 6000 --no-open --sql "SHOW ALL TABLES"

CSV and TSV, Parquet, JSON and NDJSON and .duckdb files are supported, only dist/ and the exact files you name are served with no directory listing or traversal, and requests must come from the local machine. That last constraint is the one to respect, since the launcher is designed for single user local use rather than as a shared server.

What the browser boundary rules out

The constraints are structural rather than fixable bugs, and they follow from putting the database in the tab. The data is bounded by what the browser can reach, so very large files are a question of memory and of how the file is loaded rather than of server hardware, and the row limit mentioned for the results grid is a symptom of that. Storage is bounded by the origin private file system, so persistence works and survives reloads, but it is the browser's storage quota and not a disk you administer. A second constraint is that anything requiring the official DuckDB server features or a MotherDuck account is out of reach, which the comparison table states by listing the official UI as local plus MotherDuck only for external servers, while Duck-UI supports httpserver instances. The project also lists what it can do that the official tool cannot, namely offline and air gapped operation, self hosting, embedding, kiosk mode and reading the source, and the project itself says the official tool is fine if you just want a quick local UI. A third limit is worth stating plainly: the persistence and encryption story is browser storage with AES-256-GCM in the browser for credentials, which is convenient and local, and it is not the same guarantee as a managed secret store. The project also ships a kiosk mode for publishing read only datasets, an installable PWA for offline use, and support for static hosting, GitHub Pages subpaths and air gapped setups, which tells you the deployment audience is broad.

Editorial conclusion

Adopt Duck-UI when you want to open a Parquet or CSV file and query it without installing anything or touching a server, when you need to hand a colleague a runnable link, or when you need an AI assistant that can stay inside the browser. Do not adopt it as a replacement for a server side analytics stack, since the data lives in the tab and the browser is the limit, and do not assume the official duckdb -ui feature set is covered. Before you deploy it internally, check whether the data host enables CORS, review the DUCK_UI_ environment variables for anything you plan to expose, and decide whether the AI assistant runs on WebLLM locally or sends your schema to a hosted provider.

Frequently asked questions

What is Duck-UI and how does it run DuckDB in a browser?

Duck-UI is an MIT licensed DuckDB workbench that runs on DuckDB compiled to WebAssembly, so queries execute in the browser tab rather than on a server. It offers a SQL editor, notebooks with SQL, Python and markdown cells, charts, dashboards and a local-first storage model, and there is no install, signup or backend.

How do I run Duck-UI myself?

The documented one-liner is `docker run -p 5522:5522 ghcr.io/caioricciuti/duck-ui:latest`, then open http://localhost:5522. You can also build locally with `bun run build` and serve it with the bundled launcher, for example `node cli/duck-ui.js sales.csv events.parquet`, which serves the app on 127.0.0.1 with your files queued.

Does the Duck-UI AI assistant send my data to a provider?

By default only your schema is sent, never your data, and the optional Explain results action sends a small row sample and asks for consent every time. If you use WebLLM nothing leaves the browser at all, and you can also bring OpenAI, Anthropic or any OpenAI compatible endpoint.

How do I share a Duck-UI analysis with someone?

Add a ?load= parameter to the app URL, optionally with &sql=, and the whole analysis is encoded in the link with no server involved. Openers see what will load and what will run and confirm once, and the data host must have CORS enabled.

How does Duck-UI differ from the official duckdb -ui?

The project compares them directly: Duck-UI's frontend source is MIT and entirely in the repository while the official UI's is closed, Duck-UI runs from any static host, Docker or a URL and works offline, it supports self hosting, embedding and kiosk mode, and its AI assistant takes a provider you bring rather than requiring a MotherDuck account. The official tool is described as fine for a quick local UI.

Official sources

  1. caioricciuti/duck-ui on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/caioricciuti-duck-ui.svg)](https://hysenlabs.com/projects/caioricciuti-duck-ui)

Was this useful?