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/blob
for persistent{ count }
- Features: Click button, see live count, persists across reloads.
-
Routes:
/
,/roll
,/history
,/clear
-
Parsing:
NdM+K
expressions -
UI: Input + roll button + history list + clear history
-
Improvements:
- Dynamic
BASE
detection 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
BASE
fromwindow.location.pathname
for embeds - Blob Persistence:
blob.getJSON
/blob.setJSON
for 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.