A collection of embeddable Notion widgets hosted on Val Town, allowing you to serve interactive UI directly within your Notion pagesβno external servers required.
This repo demonstrates how to build, deploy, and embed serverless widgets using Val Town Projects. Widgets include:
- Base Widget (
base_widget.ts) β Example static HTML/JS export - Click Counter (
click_counter.tsx) β Persistent click counter using blob storage - Simple Dice Roller (
dice_roller.ts) β D&D-style expression roller with history and clear functionality - Advanced Dice Roller (
advanced_dice_roller.tsx) β Adds presets, advantage/disadvantage, keep/drop, exploding dice
notionWidgets/ ββ base_widget.ts # Minimal example of a static UI val ββ click_counter.tsx # Click counter with blob persistence ββ dice_roller.ts # Simple expression-based dice roller ββ advanced_dice_roller.tsx # Full-featured dice roller with advanced mechanics ββ README.md # This documentation
- A Val Town account (free or pro) with the Projects feature enabled
- Basic familiarity with TypeScript/TSX and serverless concepts
- Notion workspace where you can embed iframes via
/embed
-
Clone or Remix this project in Val Town:
-
Click Remix on the project page, or
-
Use the Val Town CLI:
vt clone <username>/notionWidgets cd notionWidgets vt push
-
-
Publish your project (ensure itβs public or unlisted).
-
Access each widget at:
https://<your-username>.val.run/<file-name-without-extension>- E.g.:
/click_counter,/dice_roller,/advanced_dice_roller
- E.g.:
-
Embed in Notion:
- In Notion, type
/embedβ Embed URL. - Paste the widget URL (e.g.
https://<username>.val.run/dice_roller). - Resize the iframe as needed.
- In Notion, type
A minimal example exporting a static HTML page via a default export.
- Routes:
/(UI),/count(GET/POST API) - Blob storage:
std/blobfor persistent{ count } - Features: Click button, see live count, persists across reloads.
-
Routes:
/,/roll,/history,/clear -
Parsing:
NdM+Kexpressions -
UI: Input + roll button + history list + clear history
-
Improvements:
- Dynamic
BASEdetection viawindow.location.pathname - Full breakdown: individual rolls, modifiers, totals
- HTTP caching patterns (ETag) in expanded versions
- Dynamic
-
Advanced Mechanics:
- Presets: Attack, Damage, Stat, Advantage, Disadvantage
- Advantage/Disadvantage:
advdM/disdM - Exploding Dice:
NdM! - Keep/Drop:
NdMkhK/NdMklK(e.g.4d6kh3)
-
UI Enhancements:
- Styled themes (dark/light)
- Error handling in client JS
- Full history with timestamp and breakdown
- Dynamic Base Path: derive
BASEfromwindow.location.pathnamefor embeds - Blob Persistence:
blob.getJSON/blob.setJSONfor stateful data - Modular Parsing: extensible
parseExpression()covering multiple RPG mechanics - TSX Parsing: avoid nested backticks by using single-quoted JS strings and concatenation
- Add statistics dashboards (mean/median/histogram)
- Implement CSV/JSON export or clipboard copy
- Integrate with Notion API to write rolls back into a database
- Enhance accessibility and keyboard support
- Add sound/animation for tactile feedback
Enjoy building and embedding your own custom Notion widgets with Val Town! Feel free to extend and remix this template.