Skip to main content Notion blocks  for Svelte
Beat Technology AS

Notion blocks for Svelte

Wouldn't it be cool if you could use Notion as a CMS for your website? Then this is the project for you.

Notion exposes a wonderful API with many different blocks available. So we had a thought; could we use Notion as a real CMS? We were pretty happy with Notion's text formatting tools and fast editor. So this project was born.


We had a short list of goals for this project:

  • Produce semantic HTML (or as close to it as we can get).
  • Keep pages fetched from Notion performant.
  • Support all blocks that make sense.
  • Helper methods to fetch enough data about a page.

Non-goals (for now)

  • Support linking between Notion pages. We do however support external links.
  • Support page properties. This may come later.



$ npm i -D @beat-no/svelte-notion-blocks

Getting an API key from Notion

This is a bit out of scope for this article, but Notion has a good article on how to get your API key:



This guide describes the authorization flows for internal and public Notion integrations.

Note that you also have to share the pages you want to access using the API with your integration for it to work.

Retrieving a page

We strongly recommend creating an endpoint to provide the block data to the page where it should be displayed. The advantage of creating an endpoint is that you can set the data to be cached at the CDN/edge level.

The other alternative is to load the data directly from the Notion API in the load function of the page, but that does not allow for easy caching of the Notion data.

At its smallest, the endpoint you should create would look something like this (in this project, this is a simplified version of src/routes/api/page/[name]/server.ts):

import { page, agressiveCache } from '$lib/helpers';
import { Client } from '@notionhq/client/build/src';
import { json } from '@sveltejs/kit';

// get the Notion API key from environment
const client = new Client({ auth: import.meta.env.VITE_NOTION_API_KEY });

export async function GET() {
    const response = await page(client, '70d376d4fd334ef3a3095e59e0c97081');
    return json(response, agressiveCache(3600));

The page() helper method will retrieve the page, and all of its block (nested blocks if necessary). If you visit this endpoint, it will return an object with two properties: page and blocks.

Once you have this endpoint in place, you need to call it from your load function ( +page.ts):

export async function load({ fetch }) {
    const response = await fetch(`/api/page/introduction`);
    return await response.json();

Now, in your +page.svelte:

<script lang="ts">
    import { Blocks } from '@beat-no/svelte-notion-blocks';
    export let data;
    const { page, blocks } = data;

<article class="notion wrapper_for_styling">
    <Blocks {blocks} />

Helper methods


If you have a Notion database, you can use the listPages and pageFromSlug helpers from @beat-no/svelte-notion-blocks/server. To use it, you have to define a Database object to use, but its syntax is relatively simple:

import type { Database } from '@beat-no/svelte-notion-blocks'

const BlockDatabase: Database = {
	slug_property: 'Type',
	query: {
		database_id: 'd5aa56cee3114c83a608bd30f7163280',
		sorts: [
				property: 'Name',
				direction: 'ascending'
		filter: {
			and: [
					property: 'Type',
					rich_text: {
						does_not_contain: ' '
					property: 'Type',
					rich_text: {
						is_not_empty: true
  • slug_property should be the name of the database property in Notion (same capitalization as you see in the Notion UI) that will be used for uniquely identifying a database item.
  • query is the full query object from Notion’s API. It specifies which database to query, and any filters and sorts that are applied. When you use it with pageFromSlug, the helper method will add an extra filter to query.filter.and to find the correct slug.

You use this object with the helpers like this:

import { listPages, pageFromSlug } from '@beat-no/svelte-notion-blocks/server';

const client = new Client({ auth: "secret_token_from_notion" });
const pages = await listPages(client, BlockDatabase);
const { page, blocks } = await pageFromSlug(client, BlockDatabase, slug);

page will contain the properties of the page, and blocks will be a list of all the blocks on the page (including nested child blocks).

See src/routes/api/blocks/+server.ts for an example with listPages, and src/routes/api/blocks/[type]/+server.ts for an example of pageFromSlug.


You can retrieve any page with its blocks from Notion using page.

import { page } from '@beat-no/svelte-notion-blocks/server';

const { page, blocks } = await page(client, page_id);