Malloy Notebooks (.malloynb files) combine queries, results, and documentation in a single interactive document. They're the best way to explore data, build analyses, and share findings.
What Is a Malloy Notebook?
A notebook is a file with alternating cells of:
Markdown - Text, headings, explanations
Malloy code - Model definitions, queries
When you run a notebook, each code cell executes and displays results inline. The document becomes a living report.

This entire documentation website is built from .malloynb files—interweaving markdown with actual running Malloy code blocks.
Creating a Notebook
1. Create the File
In VS Code, create a new file with the .malloynb extension:
File → New File → sales_analysis.malloynb2. Add Cells
Click + Code or + Markdown to add new cells to your notebook.

Under the hood, .malloynb files are plain text with cell markers (>>>markdown or >>>malloy), so they're easy to diff and version control.
3. Run the Notebook
Click Run All at the top of the VS Code editor, or run individual cells by clicking the play button next to each one.
Use Cases
Static Dashboards
Notebooks render code blocks with descriptions. They can render any Malloy visualization—dashboards, charts, tables. Readers can inspect the code used to generate them.
Data Stories
From EDA and high-level statistics to detailed drill-down, notebooks show the flow of analysis. Use them to document your analytical journey and share insights with others.
When published via Publisher, notebooks become read-only—great for dashboards and reports.
Example Notebooks
See these notebooks for inspiration:
Names Analysis - Exploring baby name trends
Carrier Analysis - Flight carrier performance
Auto Recalls - Example of starting with an import
Tip: Press . on any GitHub page to open it in github.dev (VS Code in the browser).
Importing Models
A typical notebook has two cells: an import cell and a query cell.
Cell 1 — Import your model:
Cell 2 — Query using sources from that model:
run: flights -> { aggregate: flight_count nest: by_carrier is { group_by: carrier aggregate: flight_count limit: 5 } }
[ { "flight_count": 344827, "by_carrier": [ { "carrier": "WN", "flight_count": 88751 }, { "carrier": "US", "flight_count": 37683 }, { "carrier": "AA", "flight_count": 34577 }, { "carrier": "NW", "flight_count": 33580 }, { "carrier": "UA", "flight_count": 32757 } ] } ]
WITH __stage0 AS ( SELECT group_set, CASE WHEN group_set=0 THEN COUNT(1) END as "flight_count__0", CASE WHEN group_set=1 THEN base."carrier" END as "carrier__1", CASE WHEN group_set=1 THEN COUNT(1) END as "flight_count__1" FROM '../../documentation/data/flights.parquet' as base CROSS JOIN (SELECT UNNEST(GENERATE_SERIES(0,1,1)) as group_set ) as group_set GROUP BY 1,3 ) SELECT MAX(CASE WHEN group_set=0 THEN "flight_count__0" END) as "flight_count", COALESCE(LIST({ "carrier": "carrier__1", "flight_count": "flight_count__1"} ORDER BY "flight_count__1" desc NULLS LAST) FILTER (WHERE group_set=1)[1:5],[]) as "by_carrier" FROM __stage0
This keeps your semantic model separate from analysis notebooks.
Publishing Notebooks
Notebooks can be deployed via Publisher alongside your semantic models. Published notebooks:
Render as browsable HTML reports
Include live query results
Can be shared via URL
To publish, include .malloynb files in your package alongside .malloy models:
my-analytics/ ├── publisher.json ├── models/ │ └── orders.malloy └── notebooks/ └── monthly_report.malloynb
Tips
Start simple - Begin with a basic source, run a query, then add complexity.
Use markdown liberally - Document your analysis as you go. Future you will thank present you.
Keep models separate - For reusable logic, put it in a .malloy file and import it into notebooks.
Run cells individually - When debugging, run one cell at a time to isolate issues.
Next Steps
Building Semantic Models - Deep dive into Malloy modeling
Publishing Models - Deploy your work
Malloy in Jupyter - Use Malloy in Jupyter notebooks