Demo Astro Notion Setup

Welcome to the Astro Personal Academic website demo! This platform is designed to showcase the versatility and power of Astro, a modern web development framework. Whether you're an experienced developer or a curious individual eager to explore the world of web development, this website is your gateway to a world of endless possibilities.

📫 Subscribe to this RSS feed to stay updated with the latest content!

🔗 Home Link: https://nerdymomocat-templates.github.io/webtrotion-astro-notion-cms-website-blog

🔗 Feed URL:

🕒 Last Updated: Mon, 02 Nov 2026 04:39:36 GMT

Testing Upcoming Features Page

📚 Another Collection🏷️ Tools
Testing Upcoming Features Page
Heya! Test Page That Has Such A long Description For Short Term Testing

Last Updated:

testing custom emojis [^ft_hmm1] in this test of numbering [@sweig42]

test [@sweig42] test footnote test

Testing custom mdx component
<!-- mdx inject -->import FancyBadge from "@custom-components/FancyBadge.astro";**Remote MDX Demo**<FancyBadge title="Heads up">This section renders via a custom Astro component.</FancyBadge>

hello [@sweig42] says [a]

same [@sweig42] page block hello mention: Testing Upcoming Features Page

other page [@steward03] says!

same page but marker [b] in inline #cite(sweig42) code [^ft_blah] is not processed

Current {x,y}\{x,y\}{x,y} page block link!

⬜
Really long link test: Refractor \cite{sweig42} based off this later? https://github.com/chrismwilliams/astro-theme-cactus/commit/bd1a17c7895ec8c86cf477267b71fbb8e4635d04
caption has footnote???? [^ft_huh]
caption has footnote???? [c]
edited it [d][^ft_b][^ft_c]. some other content can be here. this is not just in paras, para [e][^ft_b1][^ft_c1] is just an example.

this is a start of child block based Current page block link! footnote Introducing Webtrotion Oct 24, 2025

why do this at all then? why it be a child-block. because see this child-block has block children and can be mixed media. o
Image uploaded to Notion

so this footnote is not just multiline, but multi-block.

all of the children of a footnote content block are part of footnote though

it can have unrelated children too which are not footnotes! they all will be after the footnote block set though [@steward03] says!

Bookmark for Just a moment...

special popover from all-footnotes page

test what @ does in various conditions, specifically what the plain text looks like. Oct 26, 2025 Oct 27, 2025, 04:00 PM Dec 1, 2025 Home

Block A links to Block C

Block C links to Block B

custom image icon callout
ttt popover with footnote on another page

ttt popover with citation on another page

testing #:~:text fragment link.)

testing #:~:text fragment link.) of the same page as above

testing a #:~:different page text fragment link

same page text fragment link but testing a different page text fragment link with text not having the marker

Tab blocks?

• hello
• hi
mk
,kmklpo,j8
lolpmjh
• hello
• hi
mk
,kmklpo,j8
lolpmjh

HTML artifact upload

Testing new linking format?

Testing page link after copied

Testing block link on a different page after copied

Testing block link on same page after copied

external website page link

same workspace page link not included here but public

What does this come as?

Footnotes

  1. hello!
  2. so [@example_website] helllloooo!!!
  3. testttt!!!
  4. this is a new line based footnote [end-of-block] Current page block link! Introducing Webtrotion Oct 24, 2025 [@steward03] says!
  5. see there can be multiple new line based footnotes [end-of-block] in same block!
    each footnote can also be of multiple lines!!

Introducing Webtrotion

📚 Personal Notes🏷️ 🪴 Potted🏷️ Guide
Introducing Webtrotion
Learn about and how to use this theme here!

Last Updated:


Are you curious on Jun 25, 2024 whether to use Notion Sites v2 or Webtrotion? Check this out! If you are checking it out Nov 1, 2025, then Webtrotion 2.0 also supports footnotes and citations (special parsing), which Notion Sites cannot render.
Support me on product hunt!
Webtrotion - Notion-based configurable static website generator | Product Hunt
New Features and Changelog

Oct 28, 2025: Comprehensive Citation and Footnote System. Webtrotion now has a complete citation and footnote system. You can now add citations from BibTeX files and have them automatically formatted. Footnotes are also supported, with popovers for easy reading.

Oct 25, 2025: Enhanced OG Images. OG images for tags and collections now include descriptions. A new automated workflow has been added to clean up template-specific files when you create a new repository from the template or pull updates from it. This keeps your project clean and free of unnecessary files.

Sep 11, 2025: Search from Query Parameters and UI improvements. You can now pre-fill the search box with a query parameter in the URL. The UI for the tags page has also been updated for a cleaner look.

Sep 10, 2025: Cache Recovery Workflow. A new GitHub Actions workflow has been added to allow you to recover your cache.

Sep 4, 2025: Upgraded Notion API. The project has been updated to use a newer version of the Notion API, ensuring better stability and compatibility with future Notion features.

Jul 6, 2024: Introducing Webtrotion. Unlike notion where your simple tables are basically just a form of displaying data, you can now search and sort your simple tables in webtrotion. Just have column headers and use <<🗂️>> in the first cell (modifiable in constants-config).
Your browser does not support the video tag.

Jul 4, 2024: Breaking change → the shortcode for HTML rendering in an iframe (if you want to use external libraries like d3.js has changed to <!DOCTYPE html> <!-- iframe --> and for injecting it directly into the body is now <!DOCTYPE html> <!-- inject -->. This helps you have code blocks that are in html and start with <!DOCTYPE html> without rendering them.

Jun 30, 2024: View transitions are back in and use ⌘K or ^K to search
Your browser does not support the video tag.
Image uploaded to Notion
Jun 28, 2024: Floating table of contents and shiki transforms for code blocks (updated to astro 4.11), and added view transitions
Image uploaded to Notion
Jun 14, 2024: Pin posts to top
Image uploaded to Notion
Jun 15, 2024: Add tag descriptions
Image uploaded to Notion
Dec 12, 2023: Add support for preview popovers
Image uploaded to Notion
Dec 4, 2023: Add support for recent posts on homepage
Image uploaded to Notion
Dec 2, 2023: Added interlinked content section
Image uploaded to Notion

Nov 29, 2023: Caching


Webtrotion is a extensively configurable, simple to install website builder, built with the Astro framework in conjunction with Notion. Use it to create an easy-to-use blogor website.

(Skip to features and setup)

Acknowledgements

This theme is built based off three major contributions:

Webtrotion Cat
Webtrotion Cat

Why Webtrotion

Webtrotion was built with the simple idea - most notion based builders, require at least one of these four things:

  • A custom domain, or being willing to use a vercel like domain
  • Just have blogposts be editable in Notion while pages are edited in the repo
  • Require some third party tools, or are not free to use.
  • Are not configurable because they are either based off third parties like super.so or the configuring requires editing multiple code files.

And I did not want that. We want something that converts well into a static site, can be hosted on github for free using the github.io domain, and can have both pages and blog posts and be configured pretty easily.

Why Notion and Astro

  • I use Notion for all my notes, and it did not make sense for me to download them into an md file, carefully figure out the logistics and push/pull with other SSGs like Quartro, Eleventy, Hugo or Jekyll.
  • I could have used another CMS but again, I use Notion, and it is easier to keep the content in one place. Notion also comes with interesting affordances that other CMS don’t: WYSIWYG for various components, block level permissions (to add drafts to post text), easy collaboration etc.
  • There are some NextJS options that kinda fulfill this criteria but I do not know NextJS and I did not want to figure it out at the moment, for example: Notion Next, Morethan-log, and Notion-Blog-NextJs

Key Features

  • Astro v5 and Tailwind v4
  • Integrates with Notion to create a website and not just a blog
  • Single file configuration
  • TailwindCSS Utility classes withNotionColorMatchingforeverything .
  • Accessible, semantic HTML markup
  • Responsive & SEO-friendly
  • Dark / Light mode, using Tailwind and CSS variables that can be modified using a single config file
  • Satori for creating open graph png images that includes your featured image
  • Pagination
  • Automatic RSS feed
  • Webmentions
  • Giscus and Bluesky comments
  • Auto-generated sitemap
  • Pagefind static search library integration
  • View Transitions
  • Pinned Posts
  • Shiki Transformers for Code Blocks
  • API request output caching on Github Actions for fast build times
  • Mini blog streams (idea copied from Linus’s stream )
  • Auto-generated related content and pages that link to this page
  • Pretty looking wikipedia like popups on hover that works with links to any block on any page.

Starting Webtrotion 2.0,

  • Footnotes and Citations[b]
  • Listing view supports both list and gallery layouts
  • Multiple authors with dedicated author pages
  • Cover images as hero backgrounds

Demo

Check out the Demo, hosted on Github using Github actions

We’re now on Webtrotion 2.0! When you clone the template, you'll get the latest version with JSON5 config, footnotes, citations, and all the new features. These docs reflect version 2.0.

Quick start

Notion Setup

  1. Duplicate this database into your Notion account. Remember to duplicate it as a standalone new page, rather than into an existing page.

    Your database title becomes the site title, the database icon becomes the favicon, and the description becomes the site description.

  2. Create a Notion integration
  3. Get your API secret
  4. Give your integration permission to the complete database you just duplicated
  5. Recommended: Get your Data Source ID. This is the preferred connection method for Webtrotion 2.0 because it's more stable. You can get it from:
    1. Open your database in the Notion app
    2. Click the ••• menu in the top right
    3. Scroll down to Manage data sources
    4. Click Copy data source ID
  6. Alternative: Get your database ID. Database ID is the 32 character alphanumeric string right after your workspace in the URL. It is the easiest to get this ID on a web browser rather than the Notion app. If you use the database ID, Webtrotion will automatically find the Data Source ID from it.
    Database id from notion url
    Database id from notion url
Which ID should I use? Data Source ID is preferred because it's more stable. Database ID works as a fallback - Webtrotion will auto-find the first Data Source ID from it. If you provide both, Data Source ID takes priority.

Github Setup

  1. Create a new repo from this template if you want to have a one and done standalone repository. If you want to be able to access and sync changes made in the template to your repository, choose to fork it instead.
    If you do not choose to add a custom domain, the name of your repository matters. If the name of the repository you create is <username>.github.io; your website will be accessible at <username>.github.io if you use github actions. If you use any other name, the website will instead be hosted at <username>.github.io/<reponame>.
    When you first fork the repository, you will see that the github action output fails. This is because we haven’t added or changed notion integration information. Don’t worry, once you add those, it will run successfully.
  2. In your repository settings:
    1. Uncheck template repository if it is checked.
    2. Turn on Discussions in features to use Giscus
    3. Go to Actions → General and set it to Allow All
    4. Go to Pages and set Source as Github Actions
    5. Go to Environments → github-pages; Scroll down to Environment Secrets, click Add New Secret. Set name as NOTION_API_SECRET and the value to the API secret you obtained in Notion Setup
    6. Very Optional (if you are tech-savvy or want to try): Get a personal access token to delete caches and set another environment secret named DELETE_CACHES_GH_TOKEN to this token
      Details
      • Obtain a personal access token that you can limit to this repository using the process mentioned here: GitHub DocsManaging your personal access tokens - GitHub Docs. You only need the repo scope and remember to set the validity to no expiration. Remember to save this token somewhere because you will not see it again.
      • In the environment secrets section, add another environment secret named DELETE_CACHES_GH_TOKEN and set it to the token you obtained in the previous step.
      • Why do this at all? Because I like cleaner lists — and here is some information from Github’s end: GitHub DocsCaching dependencies to speed up workflows - GitHub Docs
  3. Go to the actions tab, and choose I understand and want to run actions. On the left sidebar click the action “Deploy Github pages”. It will show a warning saying the scheduled action is disabled in forks. Click Enable workflow.
  4. Set up giscus as mentioned on the website giscus.app. Keep the script it produces open as we will use it in the next step.
  5. Go back to your cloned (forked or created through a template) repository.
  6. Open file constants-config.json5[c] in the web UI. This file is the complete setup file for your website. For now, we will make four major modifications:
    1. Under notion, change data-source-id to your data source id that you obtained from Notion (or use database-id as fallback)
    2. Under site-info, add your name to author (this is used for RSS feeds, OG images and metadata)
    3. Under socials, add your social media profile URLs (you should also remove the value for this-github-repo and point it to yours)
    4. Under comments → giscus, add your giscus information to data-repo and other fields. If you do not want to use Giscus, leave data-repo empty and that will disable the feature.
  7. Save the file and commit+merge to the main repo.
  8. The github action by default runs every 8 hours or on commits to the repo. This can be modified in .github/astro.yml file. You can choose any cron duration.
And we are done! You can access your website on <username>.github.io or <username>.github.io/<reponame> depending on what you chose. Checkout all Supported blocks but tl;dr all blocks are supported except child databases and child pages.
Remember, the cron schedule is by default set to every 8 hours. You can change it to run every 2 hours or if you want to push out a change immediately, you can also manually run the github action if you are deploying on github. The workflow name is “Deploy Github Pages”.

Preview

Light mode preview
Light mode preview
Dark mode preview
Dark mode preview

Notion Properties

Your database title becomes the site title, the database icon becomes the favicon, and the description becomes the site description.

Pages or posts are only published if: their Published property is checked ANDif the explicit publish date is empty or is before the current date.
PropertyUsage
PageTitle of the page or post being rendered
TagsTags displayed for the post
ExcerptDescription used in OG images and RSS feeds
CollectionCollection in which the post/page goes. Anything tagged with main or your menu-pages-collection value (default "stream") is rendered as a page.
PublishedYour post or page is only published when you tick publish
FeaturedImageImage used to generate your page’s or post’s open graph image
AuthorsUsed to specify multiple authors for a post using Multi Select (tag description can be used to specify author-url, photo and description in shortcodes)
Specific SlugThe template generates a slug using formula, but if you want a specific slug, you can specify it here
Explicit Publish DateBy default, the formula considers the date notion page was created to be publish date. You can override it by setting a date for this property.
Explicit Last Updated DateBy default, the formula considers the date notion page was last edited to be last edited date. You can override it by setting a date for this property.
RankWant to order single pages (Updates before Papers?) Set rank in ascending order
PinnedPins post to the top of any collection page, except those tagged with menu-pages-collection.
Bluesky Post LinkUse bluesky post as a commenting mechanism, see variable: bluesky-comments
Don’t rename the columns of DB. They are used in the astro code. You can reorder the columns or add any other columns you want.

Local Run

You can run the code locally by cloning the repo.

cd folderexport NOTION_API_SECRET=YOUR_KEY_HEREnpm installnpm run build #we need to build once because that is when all icons are downloadednpm run dev#use npm run build-local to reflect code changes#use npm run preview to see the final state

Extra configuration options

P.S.: You can search this simple table!

Config KeyDescription
notionNotion connection: data-source-id (preferred, more stable) or database-id (fallback, auto-finds data source if data-source-id empty)
site-infoBasic site info: author (name for RSS/OG/metadata), custom-domain (without https:// or slash, empty for Vercel/Netlify), base-path (subdirectory like "/blog", empty if root), site-author-url (URL for site author, only when author name matches site author), site-author-photo (avatar URL for site author)
site-info → authorsMulti-author support: enable-author-pages (builds /authors/ index and /authors/[author]/ pages), only-when-custom-authors (only show bylines and author pages when at least one post has a non-default author)
collections-and-listingsPost organization: home-page-slug (default "home"), recent-posts-on-home-page (show 5 recent posts), number-of-posts-per-page (default 10), menu-pages-collection (pages in nav, default "main"), full-preview-collections (array for stream-style display), hide-underscore-slugs-in-lists (hide _ prefixed from lists/sitemap/search), listing-view ("list" for traditional or "gallery" for card-based grid with FeaturedImage thumbnails)
theme → colorsRGB values (with spaces, e.g. "255 255 255") for light/dark modes: bg (background), text (main text), link (links), accent (primary highlights), accent-2 (secondary highlights), quote (blockquotes)
theme → fontfamily-google-fontsFont names only (no URLs, no escaping): sans-font-name (body/UI font, can be sans or serif), mono-font-name (code blocks). Auto-loads weights [400, 500, 600, 700] and styles [normal, italic]
theme → cover-as-hero-backgroundUse cover image as background behind hero/post preview
socialsSocial profile URLs (keys are fixed, match icons): email, github, googlescholar, semanticscholar, dblp, substack, facebook, twitter, threads, instagram, mastodon, bluesky, calendar, kofi, buy-me-a-coffee, this-github-repo
tracking → google-analyticsGoogle Analytics: use-ga (enable GA tracking), public-ga-tracking-id (GA4 Measurement ID like G-XXXXXXXXXX)
tracking → umamiUmami analytics: use-umami (enable), data-website-id (website ID from Umami dashboard), self-hosted (true for self-hosted instance), self-hosted-umami-url (script URL for self-hosted)
tracking → google-search-console-html-tagGoogle Search Console verification: paste the 'content' attribute from verification meta tag
og-setupOG image generation: columns (1 = single column with bg, 2 = side-by-side), excerpt (include excerpt), title-font-name (any Google font), title-font-weight (default 400), footnote-font-name (author/date font), footnote-font-weight (default 400)
comments → giscusGitHub Discussions comments: data-repo (username/repo), data-repo-id (from giscus.app), data-category (discussion category name), data-category-id (from giscus.app), data-mapping (how to map pages, e.g. "pathname"), data-input-position ("top" or "bottom"), data-reactions-enabled (enable emoji reactions)
comments → bluesky-commentsBluesky comments: show-comments-from-bluesky (enable), auto-search-for-match with turn-on-auto-search (auto-find posts), author (your Bluesky handle), echo-feed-emoji (emoji marker for blog posts)
comments → webmentionWebmentions: webmention-api-key (API key from webmention.io), webmention-link (endpoint URL)
block-renderingContent display: enable-lightbox (click images for fullscreen), full-width-social-embeds (embeds span full width on small screens), optimize-images (convert to WebP, slower builds but smaller files), process-content-to-markdown (convert all pages/posts to markdown format alongside HTML output for use with LLMs and other tools)
shortcodesSpecial markers in Notion code blocks: html-render (render HTML in iframe, default <!DOCTYPE html> <!-- iframe -->)[d], html-inject (inject raw HTML, default <!DOCTYPE html> <!-- inject -->), expressive-code (not implemented), shiki-transform (advanced highlighting, default <<shiki-transform>>), mdx-inject (MDX compilation, default <!-- mdx inject -->), table (datatable features, default <<🗂️>>)
shortcodes → author-descAuthor metadata shortcodes in multi-select description field: author-url (start/end markers <<author-url>>), author-photo-url (start/end markers <<author-photo-url>>). Remaining text is the bio
auto-extracted-sections → footnotesFootnotes: sitewide-footnotes-page-slug (legacy manual page slug, default "all-footnotes"), in-page-footnotes-settings with enabled (toggle system), source (only one true: end-of-block for definitions at block end, inline-latex-footnote-command for \\footnote{content}, start-of-child-blocks for first n child blocks, block-comments for Notion comments with Read permission, block-inline-text-comments not yet implemented), marker-prefix (default "ft" for [^ft_1], does not apply to inline-latex-footnote-command), generate-footnotes-section (collate at page bottom), show-in-margin-on-large-screens (margin on large, popovers on small)
auto-extracted-sections → interlinked-contentInterlinked content at page bottom (formerly references): site-links-in-page (links to other site pages), external-links-in-page (external website links), media-and-file-links-in-this-page (embedded media files), links-to-this-page (backlinks from other pages). Popovers enabled by default
auto-extracted-sections → citationsCitations: add-cite-this-post-section (add "Cite this page" section), extract-and-process-bibtex-citations with enabled (toggle BibTeX processing), bibtex-file-url-list (array of .bib URLs: GitHub Gist/repo, Dropbox, Google Drive; GitHub links check timestamps, Dropbox/Drive fetch on every build), in-text-citation-format ([@key] pandoc cite\{key\} LaTeX / #cite(key) Typst, renders as [firstName et al, year] or [1][2]), bibliography-format (only one true: simplified-ieee for numbered ICML/NeurIPS style, apa for author-year ACL style), generate-bibliography-section (collate at page end), show-in-margin-on-large-screens (margin on large ≥1024px, popovers on small)
external-contentPull external content from GitHub via Notion's "External URL" field: enabled (toggle feature), sources with markdown (GitHub folder URL where each subfolder is a markdown post), mdx (GitHub folder URL for MDX posts), html (GitHub folder URL for HTML posts), custom-components (GitHub folder for MDX imports, downloaded to src/external-content/custom-components, also usable via mdx-inject shortcode)
redirects301 redirects: object mapping old paths to new (e.g., "/old.html": "/new/") for SEO when migrating content
Webtrotion 1.0 extra configuration options
KeyValue
public-ga-tracking-idYour google tracking property id. Steps here.
google-search-console-html-tagContent value of search console property verification tag. More here
webmentionWebmention API Key and link obtained from https://webmention.io/
custom-domainIf you want to host the site somewhere else
base-pathOr subdomain inside the custom domain
shortocdesThere are 3 shortcodes atm, two supported, two in works. html shortcode is used to embed html code blocks that start with this line directly into the page. shiki-transform is used to transform code blocks into more expressive representations. table is used to set the identifier for data-tables enabling search and sort.
referencesTo show related pages, external links, and mentioned media at the end of the post. Set popovers to true to display a snippet when hovering over a post link in the body.
themeColors for light mode and dark mode
fontfamily-google-fontsCombined URL link for all fonts you want to use from google fonts. Remember to escape spaces in font names if needed
number-of-posts-per-pageNumber of posts in a page for pagination purposes
enable-lightboxSet to true if you want people to be able to click on your image to open in a new tab
request-timeout-msTimeout for API requests
menu-pages-collectionCollection select value in Notion database that decides if these are pages or blogposts. Set this to value you use for pages.
heading-blocksWhich top-level blocks blocks should form the table of contents on right
full-preview-collectionsStream like view, where each post is a mini-blog in the same page as a scrollable page instead of being links to individual pages. Any Collection name added to this list will be rendered as stream view.
hide-underscore-slugs-in-listsIf the slug starts with _, hide those in post lists, rss and sitemap, but still render them, so that you can share them
home-page-slugBy default is set to home but can be anything that you want for the renderer to recognize which is your home page from the database
og-setupOpen graph setup refers to the images displayed when sharing links. It includes excerpts and creates two columns if a featured image is available. You can customize the title and footnote fonts for the og-image, particularly for non-English blogs. Ensure that each URL has only one font and, if it has a weight, the weight is above 400
optimize-imagesConverts images to next-gen formats like webp for more responsive sites
redirectsIntentional redirects, especially if you are moving systems that astro should redirect to
all-footnotes-page-slug Defaults to _all-footnotes. This page is not visible in post list because it starts with a _ but you can use this show popup blocks on any of your pages.
bluesky-commentsshow-comments-from-bluesky to turn on/off a Bluesky comment section. auto-search-for-match searches your profile for parent posts mentioning the link, controlled by turn-on-auto-search, with author specifying your Bluesky handle; echo-feed-emoji filters these searches.

License

MIT

Notes

Don’t rename the columns of DB. They are used in the astro code. You can reorder the columns or add any other columns you want.
Remember, the cron schedule is by default set to every 8 hours. You can change it to run every 2 hours or if you want to push out a change immediately, you can also manually run the github action if you are deploying on github. The workflow name is “Deploy Github Pages”.
Update Jun 19, 2024: I included a keep alive workflow which will hopefully work to keep the action running without any manual intervention. Fingers crossed 🤞.

Webtrotion relies on GitHub Actions if you're deploying on GitHub Pages. If you don't commit any changes to the repository, remember to manually enable the workflow every 60 days.

Screenshot of the email you’ll get warning you about the workflow being disabled.
[GitHub] The "Deploy to GitHub Pages" workflow in nerdymomocat/nerdymomocat.github.io will be disabled soon
[GitHub] The "Deploy to GitHub Pages" workflow in nerdymomocat/nerdymomocat.github.io will be disabled soon
Aggressive Caching

Remember that the setup uses aggressive caching for github actions. These caches are public. If you want to remove caches, you can go to github action workflow and manually delete the caches

Multilingual/Non-English blogs

Open graph setup refers to the images displayed when sharing links. It includes excerpts and creates two columns if a featured image is available. You can customize the title and footnote fonts for the og-image, particularly for non-English blogs. Just specify any Google font name (no URLs needed starting version 2.0), and set the font weight if desired (default is 400).


If you end up using webtrotion, please consider buying me a coffee here:


Footnotes

  1. From September 2025 onwards, AI assistants have been used to develop complete features. Before this, GitHub Copilot and chat-based AI tools assisted with code writing but not full feature development.
  2. Just like these!
  3. For Webtrotion before 2.0, open constants-config.json and configure four essential settings: change DATABASE_ID to your Notion database ID, add your name to AUTHOR (used for HTML semantics and OG images), add your social media links, and add your Giscus information to the GISCUS key (or disable it by removing the value for data-repo in GISCUS).
  4. Notion HTML embed blocks are now supported by default as iframes, and the height of iframes is auto-calculated to a clamped value

Title but no excerpt or tags works

📚 Stream
Title but no excerpt or tags works

Last Updated:

Content [@sweig42] here [a]?


Footnotes

  1. hmm test?

Supported blocks

📚 Personal Notes🏷️ Information🏷️ Webtrotion🏷️ 🌲 Evergreen🏷️ Guide
Supported blocks
Almost all of them!
ByNerdy Momo Cat

Last Updated:
Information Webtrotion 🌲 Evergreen Guide

For using the template and the corresponding code, go to Introducing Webtrotion
The main thing to remember is it does not support child databases and child pages.

Heading 1

Catus felis silvestris, murrarum et gattonum, in domibus humanorum feliciter vivere solet, mollibus pulvinis dormiens et lanae globulis ludens.

Heading 2

Catus felis silvestris, murrarum et gattonum, in domibus humanorum feliciter vivere solet, mollibus pulvinis dormiens et lanae globulis ludens.

Heading 3

Catus felis silvestris, murrarum et gattonum, in domibus humanorum feliciter vivere solet, mollibus pulvinis dormiens et lanae globulis ludens.

Heading 4

Catus felis silvestris, murrarum et gattonum, in domibus humanorum feliciter vivere solet.

Toggleable Heading 1

contents

Toggleable Heading 2

content

Toggleable Heading 3
Toggleable Heading 4

Paragraph

Paragraph

bold

italic

Underline

Strike

link

code

← No access page

Empty Block below

color

color background

common latex code 4=54=54=5

colored block

colored text with colored block

Color Test

NameText LM (Hex)Text DM (Hex)BG LM (Hex)BG DM (Hex)Select LM (Hex)Select DM (Hex)
Default37352FFFFFFF (90%)FFFFFF2F3437CECDCA (50%)505558
Gray9B9A97979A9B (95%)EBECED454B4E9B9A97 (40%)979A9B (50%)
Brown64473A937264E9E5E34340408C2E00 (20%)937264 (50%)
OrangeD9730DFFA344FAEBDD594A3AF55D00 (20%)FFA344 (50%)
YellowDFAB01FFDC49FBF3DB59563BE9A800 (20%)FFDC49 (50%)
Green0F7B6C4DAB9ADDEDEA354C4B00876B (20%)4DAB9A (50%)
Blue0B6E99529CCADDEBF13649540078DF (20%)529CCA (50%)
Purple6940A59A6DD7EAE4F2443F576724DE (20%)9A6DD7 (50%)
PinkAD1A72E255A1F4DFEB533B4CDD0081 (20%)E255A1 (50%)
RedE03E3EFF7369FBE4E4594141FF001A (20%)FF7369 (50%)
Background

the lazycatmeowsandmeowsattheflailingkitten

Text

the lazycatmeowsandmeowsattheflailingkitten

Bulleted List

  • Bulleted List
  • Uni
  • Tako
    • AAA
    • BBB

Numbered List

  1. Numbered List
  2. Kani
  3. Ika
    1. 111
    2. 222
      1. Third!

To Do

✅
To-do 1
⬜
Todo 2
⬜
A bit complicated now
⬜
A bit more
complicated now?

What if child has no box?

What if there is suddenly a heading?

💡
Huh?
⬜
Oh

Special Parsing for Text

Footnotes [a] and Citations, for example if we wanted to know why Tufte style footnotes are nice [@balkin1988footnote].

Column List

Column 1

External image

Column 2

External image

Columns inside columns

Column 1

Column 1 still

Column 2

Column 2a

Column2 b

Tab block

Tab 1Tab 2Tab 3

Here is some content!

And so is here

Divider


Callout

ℹ️
Callout
Line break
Hello
⬜
SO many callout tests!

Like cols

And more cols

Colored Callout and background color
External image

Code

const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<h1>Hello, world!</h1>);
This is code caption.
a=32if a:	x =23

Also supports shiki transformers:

What you write

# <<shiki-transform>> # use this keyword to tell it to transform codea=32if a: 	x =23

What you get

a=32if a: 	x =23

CodePen Embed

Code with Mermaid diagram

Trying HTML injection

Remember this directive can be changed, check out Introducing Webtrotion

Hover Me
Pasta

Spaguetti Bolognese

30 Mins   |   1 Serving

A random fancy hover card (source)

Trying HTML rendering

Remember this directive can be changed, check out Introducing Webtrotion

Quote

Quote Block
Colored Quote Block

Equation

Inline equation here 1+q2(1−q)+q6(1−q)(1−q2)+⋯=∏j=0∞1(1−q5j+2)(1−q5j+3),for ∣q∣<1.1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}, \quad\quad \text{for }\lvert q\rvert<1.1+(1−q)q2​+(1−q)(1−q2)q6​+⋯=∏j=0∞​(1−q5j+2)(1−q5j+3)1​,for ∣q∣<1. for testing

Block equation below.

ΔE00∗=(ΔL′kLSL)2+(ΔC′kCSC)2+(ΔH′kHSH)2+RTΔC′kCSCΔH′kHSH\Delta E^*_{00} = \sqrt{ \Big(\frac{\Delta L'}{k_LS_L}\Big)^2 + \Big(\frac{\Delta C'}{k_CS_C}\Big)^2 + \Big(\frac{\Delta H'}{k_HS_H}\Big)^2 + R_T \frac{\Delta C'}{k_CS_C} \frac{\Delta H'}{k_HS_H} }ΔE00∗​=(kL​SL​ΔL′​)2+(kC​SC​ΔC′​)2+(kH​SH​ΔH′​)2+RT​kC​SC​ΔC′​kH​SH​ΔH′​​

Table

Know that table column/row colors do not show up in Notion API

SimpleTableIkaTakoShake
12345
abcde
ABCDE

File

A file about XYZ Download Preview
Indented File
PDFTriage: Question Answering over Long, Structured Documents Download Preview

Toggle

Toggle

This is toggled details.

  • 1
  • 2
  • 3
Toggle inside toggle

More toggles!

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut volutpat nunc. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut volutpat nunc.

Long enough toggle to see what happens to the marker here.

Link to page

Introducing Webtrotion

Synced Block

♻️
Synced Block
♻️
Synced Block
  • 1
  • 2
  • 3
♻️
Synced Block
♻️
Synced Block
  • 1
  • 2
  • 3

Synced block from a different page

New Features and Changelog

Oct 28, 2025: Comprehensive Citation and Footnote System. Webtrotion now has a complete citation and footnote system. You can now add citations from BibTeX files and have them automatically formatted. Footnotes are also supported, with popovers for easy reading.

Oct 25, 2025: Enhanced OG Images. OG images for tags and collections now include descriptions. A new automated workflow has been added to clean up template-specific files when you create a new repository from the template or pull updates from it. This keeps your project clean and free of unnecessary files.

Sep 11, 2025: Search from Query Parameters and UI improvements. You can now pre-fill the search box with a query parameter in the URL. The UI for the tags page has also been updated for a cleaner look.

Sep 10, 2025: Cache Recovery Workflow. A new GitHub Actions workflow has been added to allow you to recover your cache.

Sep 4, 2025: Upgraded Notion API. The project has been updated to use a newer version of the Notion API, ensuring better stability and compatibility with future Notion features.

Jul 6, 2024: Introducing Webtrotion. Unlike notion where your simple tables are basically just a form of displaying data, you can now search and sort your simple tables in webtrotion. Just have column headers and use <<🗂️>> in the first cell (modifiable in constants-config).
Your browser does not support the video tag.

Jul 4, 2024: Breaking change → the shortcode for HTML rendering in an iframe (if you want to use external libraries like d3.js has changed to <!DOCTYPE html> <!-- iframe --> and for injecting it directly into the body is now <!DOCTYPE html> <!-- inject -->. This helps you have code blocks that are in html and start with <!DOCTYPE html> without rendering them.

Jun 30, 2024: View transitions are back in and use ⌘K or ^K to search
Your browser does not support the video tag.
Image uploaded to Notion
Jun 28, 2024: Floating table of contents and shiki transforms for code blocks (updated to astro 4.11), and added view transitions
Image uploaded to Notion
Jun 14, 2024: Pin posts to top
Image uploaded to Notion
Jun 15, 2024: Add tag descriptions
Image uploaded to Notion
Dec 12, 2023: Add support for preview popovers
Image uploaded to Notion
Dec 4, 2023: Add support for recent posts on homepage
Image uploaded to Notion
Dec 2, 2023: Added interlinked content section
Image uploaded to Notion

Nov 29, 2023: Caching

All Mentions

Dates

Dates: Nov 23, 2023, 05:27 AM Nov 22, 2023 Nov 17, 2023 to Nov 22, 2023 Nov 23, 2023, 05:00 PM and People:

I did something very normal Nov 22, 2023 which is a huge deal!

Linked Blocks and Pages

This links to heading Column List

But we can also have links that are not on direct children of the page, right?

This inside toggle links to heading Column List

This links to a heading in another webpage

This links to a heading in same page but an internal block (not main page, nested in)

This links to block Ika (Now supported)

This links to a block in another same site page

Linked page with URL

What are complete links?

Link to main pages

Home

But paste link as mention doesn’t work: Mind map & Outline Ideas - Escape

Image

Image uploaded to Notion
Caption for an image
Caption for an image
Image uploaded to Notion

Audio

Your browser does not support the audio element.
Your browser does not support the audio element.

Link Preview & Bookmark

Other Embeds

Twitter

Mentions tweet
And a caption for the tweet!

Spotify

I got a blank space baby and I’ll write your name 😉

tldraw

Raindrop

YouTube

And a caption for youtube video

Tiktok

Instagram

Shelby Grace Instagram with orange hair

Maps

Using the embed link by google

Captions work?

Using general link by google (it auto converts)

Using Notion embed

Using HTML directive

In columns

Loom

Loom/any video embed using Notion’s option is not supported at the moment

But it works if you use their embed URL

Direct Uploaded Video

Your browser does not support the video tag.
This is a direct uploaded video caption!
Known stuff that is not supported/has bugs
  1. Multiple injections on a single page if not handled carefully, have clashes can have a problem!
  2. Resizing for embedded html is a problem, for things like d3.js, but does it matter? How does distil handle it?
  3. If you use npm run dev without npm run build, some things may not be download leading to:
    Image uploaded to Notion
  4. CSS generation is also now associated with build. Run the build command once before running dev please.

  1. Table column colors are not supported through API
  2. Block mentions are not supported through API
  3. Comments are not supported
  4. Word breaks are an issue on smaller screen if there is no viable place to break them. Currently I am using word-wrap:normal;
  5. I do not plan to add support for inline databases but I’d love if anyone wants to contribute?
  6. Child Pages are too much work to support and I do not have a personal usecase for it!
  7. Column widths and embed widths are not exposed in API
  8. Backlinks are not exposed in API so I do not know how to do Links to This Note
  9. Right now, if I click on a tag from “unlisted” URL and that tag is only attached to that post, no tag page is generated, and I think that isn’t a bad thing!
  10. If you post a parent block with child sync block to another workspace, it will not show up in UI but does show up in API causing warnings/errors
  11. Heading ids are generated based off text. If you have two headings with same names, that is an issue. I can change it to use block id, but then they are illegible to anyone using an href link for them to share.

Footnotes

  1. Footnotes can be added like this. Please check here for details.

sit amet 我爱你 with 愛してます

📚 Stream🏷️ Note-Taking🏷️ Guide🏷️ Tools🏷️ 我爱你
sit amet 我爱你 with 愛してます
This is lorem ipsum


Lorem ipsum content [a] with an image and a random file [@sweig42] below

import process from "node:process";import { TodoistApi } from "npm:@doist/todoist-api-typescript";import { Client } from "npm:@notionhq/client";const TODOIST_API_KEY = process.env.TODOIST_API_KEY;const todoistapi = new TodoistApi(TODOIST_API_KEY);const NOTION_API_KEY = process.env.NOTION_API_KEY;const notion = new Client({  auth: NOTION_API_KEY,});var add_to_notion_todoist_project_id = "PROJECT_ID_HERE";var todoist_dict_mapping = {  "habit": {    "todoist-section-id": "SECTION_ID_HERE",    "notion-map-type": "page",    "notion-id": "PAGE_ID_HERE",  },  "papers": {    "todoist-section-id": "SECTION_ID_HERE",    "notion-map-type": "database",    "notion-id": "DB_ID_HERE",  },};function getNotionId(section_id) {  if (!section_id) {    return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];  }  for (var key in todoist_dict_mapping) {    if (todoist_dict_mapping[key]["todoist-section-id"] === section_id) {      return [        todoist_dict_mapping[key]["notion-map-type"] || todoist_dict_mapping["dump"]["notion-map-type"],        todoist_dict_mapping[key]["notion-id"] || todoist_dict_mapping["dump"]["notion-id"],      ];    }  }  return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];}function convertDateObject(due) {  function convertToISOWithOffset(datetimeStr, timezoneStr) {    const date = new Date(datetimeStr);    const [, sign, hours, minutes] = timezoneStr.match(/GMT ([+-])(\d{1,2}):(\d{2})/);    date.setUTCMinutes(date.getUTCMinutes() + (parseInt(hours) * 60 + parseInt(minutes)) * (sign === "+" ? 1 : -1));    return date.toISOString().split(".")[0]      + `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;  }  const formatDate = (date, datetime, timezone) => {    let isoString = datetime ? datetime : date;    if (timezone && timezone.startsWith("GMT") && timezone.length > 3) {      return convertToISOWithOffset(datetime, timezone);    } else {      return isoString;    }  };  return {    start: due ? formatDate(due.date, due.datetime, due.timezone) : new Date().toISOString(),    end: null,    time_zone: due && due.datetime && due.timezone && due.timezone.startsWith("GMT") && due.timezone.length > 3      ? null      : (due && due.datetime && due.timezone ? due.timezone : "America/Los_Angeles"),  };}async function addCalloutToNotionPage(page_id, content, date) {  console.log(JSON.stringify(date));  const response = await notion.blocks.children.append({    block_id: page_id,    children: [{      "callout": {        "rich_text": [{          "type": "mention",          "mention": {            "type": "date",            "date": date,          },        }],        "icon": {          "type": "external",          "external": {            "url": "https://www.notion.so/icons/circle-dot_lightgray.svg",          },        },        "children": [{          "paragraph": {            "rich_text": [{              "text": {                "content": content,              },            }],          },        }],      },    }],  });  console.log(JSON.stringify(response));}async function addPageToNotionDatabse(database_id, content) {  const response = await notion.pages.create({    "parent": {      "type": "database_id",      "database_id": database_id,    },    "properties": {      "Name": {        "title": [{          "text": {            "content": content,          },        }],      },    },  });}export default async function(interval: Interval) {  var tasks = await todoistapi.getTasks({    projectId: add_to_notion_todoist_project_id,  });  for (const task of tasks) {    console.log(task);    const [mappedNotionType, mappedNotionId] = getNotionId(task.sectionId);    if (mappedNotionId)    {      if (mappedNotionType == "page" && mappedNotionId) {        addCalloutToNotionPage(mappedNotionId, task.content, convertDateObject(task.due));      }      else if (mappedNotionType == "database" && mappedNotionId) {        addPageToNotionDatabse(mappedNotionId, task.content);      }      todoistapi.deleteTask(task.id);    }  }}
Remember this code is in format of val town and hence the weird imports. You would need to switch that to require probably (I ain’t good at JS)
Code in case you want to host somewhere else
import process from "node:process";import { TodoistApi } from "npm:@doist/todoist-api-typescript";import { Client } from "npm:@notionhq/client";const TODOIST_API_KEY = process.env.TODOIST_API_KEY;const todoistapi = new TodoistApi(TODOIST_API_KEY);const NOTION_API_KEY = process.env.NOTION_API_KEY;const notion = new Client({  auth: NOTION_API_KEY,});var add_to_notion_todoist_project_id = "PROJECT_ID_HERE";var todoist_dict_mapping = {  "habit": {    "todoist-section-id": "SECTION_ID_HERE",    "notion-map-type": "page",    "notion-id": "PAGE_ID_HERE",  },  "papers": {    "todoist-section-id": "SECTION_ID_HERE",    "notion-map-type": "database",    "notion-id": "DB_ID_HERE",  },};function getNotionId(section_id) {  if (!section_id) {    return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];  }  for (var key in todoist_dict_mapping) {    if (todoist_dict_mapping[key]["todoist-section-id"] === section_id) {      return [        todoist_dict_mapping[key]["notion-map-type"] || todoist_dict_mapping["dump"]["notion-map-type"],        todoist_dict_mapping[key]["notion-id"] || todoist_dict_mapping["dump"]["notion-id"],      ];    }  }  return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];}function convertDateObject(due) {  function convertToISOWithOffset(datetimeStr, timezoneStr) {    const date = new Date(datetimeStr);    const [, sign, hours, minutes] = timezoneStr.match(/GMT ([+-])(\d{1,2}):(\d{2})/);    date.setUTCMinutes(date.getUTCMinutes() + (parseInt(hours) * 60 + parseInt(minutes)) * (sign === "+" ? 1 : -1));    return date.toISOString().split(".")[0]      + `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;  }  const formatDate = (date, datetime, timezone) => {    let isoString = datetime ? datetime : date;    if (timezone && timezone.startsWith("GMT") && timezone.length > 3) {      return convertToISOWithOffset(datetime, timezone);    } else {      return isoString;    }  };  return {    start: due ? formatDate(due.date, due.datetime, due.timezone) : new Date().toISOString(),    end: null,    time_zone: due && due.datetime && due.timezone && due.timezone.startsWith("GMT") && due.timezone.length > 3      ? null      : (due && due.datetime && due.timezone ? due.timezone : "America/Los_Angeles"),  };}async function addCalloutToNotionPage(page_id, content, date) {  console.log(JSON.stringify(date));  const response = await notion.blocks.children.append({    block_id: page_id,    children: [{      "callout": {        "rich_text": [{          "type": "mention",          "mention": {            "type": "date",            "date": date,          },        }],        "icon": {          "type": "external",          "external": {            "url": "https://www.notion.so/icons/circle-dot_lightgray.svg",          },        },        "children": [{          "paragraph": {            "rich_text": [{              "text": {                "content": content,              },            }],          },        }],      },    }],  });  console.log(JSON.stringify(response));}async function addPageToNotionDatabse(database_id, content) {  const response = await notion.pages.create({    "parent": {      "type": "database_id",      "database_id": database_id,    },    "properties": {      "Name": {        "title": [{          "text": {            "content": content,          },        }],      },    },  });}export default async function(interval: Interval) {  var tasks = await todoistapi.getTasks({    projectId: add_to_notion_todoist_project_id,  });  for (const task of tasks) {    console.log(task);    const [mappedNotionType, mappedNotionId] = getNotionId(task.sectionId);    if (mappedNotionId)    {      if (mappedNotionType == "page" && mappedNotionId) {        addCalloutToNotionPage(mappedNotionId, task.content, convertDateObject(task.due));      }      else if (mappedNotionType == "database" && mappedNotionId) {        addPageToNotionDatabse(mappedNotionId, task.content);      }      todoistapi.deleteTask(task.id);    }  }}
Remember this code is in format of val town and hence the weird imports. You would need to switch that to require probably (I ain’t good at JS)
Image uploaded to Notion
sample2.html Download

Check [@impossible]


Footnotes

  1. test footnote for stream page