Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Notion

To integrate Notion with Lovable, create Supabase Edge Functions that authenticate using a Notion internal integration token, then proxy API calls for database queries, page creation, and block manipulation. Store your integration token in Cloud Secrets to build knowledge base viewers, CMS-powered content tools, and database-driven apps in Lovable. Notion also offers an MCP personal connector for build-time AI context.

What you'll learn

  • How to create a Notion internal integration and configure page sharing for API access
  • How to create a Supabase Edge Function that queries Notion databases and fetches page content
  • How to build a Notion-powered content display component that renders Notion blocks in React
  • How to create and update Notion database records from your Lovable application
  • How to use Notion as a CMS backend for dynamic content in Lovable-built applications
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read75 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

To integrate Notion with Lovable, create Supabase Edge Functions that authenticate using a Notion internal integration token, then proxy API calls for database queries, page creation, and block manipulation. Store your integration token in Cloud Secrets to build knowledge base viewers, CMS-powered content tools, and database-driven apps in Lovable. Notion also offers an MCP personal connector for build-time AI context.

Build Notion-Powered Apps and Knowledge Base Tools in Lovable

Notion has become the default knowledge management platform for modern teams — PRDs, meeting notes, decision logs, content calendars, and project wikis all live in Notion. Building a Lovable application that reads from and writes to Notion creates powerful possibilities: a customer-facing knowledge base powered by your internal Notion wiki, a project status dashboard that pulls from your Notion project database, or a content publishing workflow that reads from a Notion content calendar.

Notion's API uses a block-based content model that mirrors its visual editor. Every piece of content in Notion — a heading, a paragraph, a bulleted list item, a table row — is a block with a type and properties. Databases in Notion are collections of pages, each with a structured schema of properties (text, number, select, multi-select, date, relation, rollup, and more). The API lets you query databases with filters and sorts, fetch page content as block trees, and create or update pages and blocks. Understanding this model is essential for building effective Notion integrations.

Notion also offers an MCP personal connector within Lovable, which provides a different capability: when the MCP connector is configured, Lovable's AI agent can read your Notion pages and databases as build-time context to generate more accurate code matching your specifications. This is separate from the runtime Edge Function integration — the MCP connector helps the AI understand your requirements, while Edge Functions let your deployed application interact with Notion data at runtime. This guide covers the runtime integration via Edge Functions.

Integration method

Edge Function Integration

Notion integration in Lovable uses Supabase Edge Functions that authenticate via a Notion internal integration token passed as a Bearer authorization header. The Edge Functions proxy Notion API calls for database queries, page creation, and block manipulation, keeping the integration token encrypted in Cloud Secrets and accessible only via Deno.env.get(). React components in your Lovable app fetch and display Notion content through the proxy without any token exposure to the browser.

Prerequisites

  • A Notion account with at least one workspace and some databases or pages to connect to
  • A Notion internal integration created at notion.so/my-integrations with read and optionally write access
  • All Notion pages and databases you want to access must be shared with your integration (each page needs to be individually shared or a parent page shared at a high level)
  • A Lovable project with Lovable Cloud enabled
  • Familiarity with Notion's block-based content model — understanding that databases contain pages and pages contain blocks will help you design your Edge Function and UI

Step-by-step guide

1

Create a Notion internal integration and configure page access

Go to notion.so/my-integrations and click 'New integration'. Give your integration a name (e.g., 'Lovable App') and select the workspace it should have access to. Under capabilities, select what your integration needs — 'Read content' is required for displaying data, and 'Update content' and 'Insert content' are needed for write operations. For a read-only knowledge base, read content is sufficient. Click 'Submit' to create the integration, and on the next page copy the Internal Integration Secret — this is your API token. It starts with 'ntn_' or 'secret_' depending on the Notion API version. Now the critical step that many developers miss: Notion integrations do not automatically have access to any workspace content. You must explicitly share each page or database you want to access with your integration. Open Notion, navigate to the page or database you want to connect, click the three-dot menu in the top right (or the 'Share' button), and in the 'Connections' section select your integration by name. For databases, sharing the parent page that contains the database shares all databases inside it. If you share a top-level workspace page with your integration, all child pages become accessible, which simplifies setup for large workspaces. Any page not shared with the integration will return a 404 error when queried via the API — a common source of confusion for developers new to Notion's permission model.

Pro tip: To avoid permission errors, share a high-level page that is a parent of all the content you want to access. Changes to the parent sharing automatically apply to all child pages and databases.

Expected result: A Notion internal integration is created with your API token, and all relevant pages and databases are shared with the integration.

2

Store the Notion integration token in Cloud Secrets

In your Lovable project, open the Cloud tab by clicking '+' next to the Preview panel, then navigate to the Secrets section. Click 'Add Secret' and create a new secret named NOTION_TOKEN with your Notion integration secret value. This is the only credential Notion requires for its internal integration authentication pattern. The token grants access to all pages and databases explicitly shared with your integration — it does not provide access to your entire Notion workspace, only the content you have chosen to share. This selective permission model makes it safer than a full workspace API key, as you control exactly what is accessible. You may also want to add NOTION_DATABASE_ID as a second secret containing the ID of your primary Notion database. Database IDs are the UUID visible in the Notion page URL — for a database at notion.so/workspace/some-title-abc123def456, the ID is 'abc123def456' (or with hyphens as a standard UUID format). Storing the database ID as a secret rather than in your code makes it easy to swap databases without redeploying. Lovable's SOC 2 Type II certified Cloud Secrets storage ensures these tokens are encrypted at rest and never accessible from client-side code or browser network requests.

Pro tip: Notion database IDs can also be found by opening the database as a full page, clicking Share, and copying the link — the UUID in the URL is the database ID.

Expected result: NOTION_TOKEN (and optionally NOTION_DATABASE_ID) are stored in Cloud Secrets.

3

Create the Notion API proxy Edge Function

Ask Lovable to create a Supabase Edge Function called notion-api that proxies requests to the Notion API. The function reads the integration token from Deno.env.get('NOTION_TOKEN'), accepts a path (like 'databases/{id}/query' or 'pages/{id}'), HTTP method, and optional request body, constructs the full Notion API URL as https://api.notion.com/v1/{path}, adds the Authorization: Bearer header, the required Notion-Version header (set to '2022-06-28' for the current stable API version), and a Content-Type: application/json header. It forwards the request and returns the Notion API response. Key endpoints to support include POST /databases/{id}/query for database queries with filters and sorts, GET /pages/{id} for page metadata and properties, GET /blocks/{id}/children for page block content, POST /pages for creating new database records, and PATCH /pages/{id} for updating page properties. The Notion API response structure varies by endpoint — database queries return a results array of page objects, block children return a results array of block objects with type-specific content. Build the Edge Function to handle any path generically rather than hardcoding specific endpoints, keeping it flexible for all Notion API operations.

Lovable Prompt

Create a Supabase Edge Function called notion-api. Read NOTION_TOKEN from Deno.env.get(). Accept path, method (default GET), and optional body in the request JSON. Call https://api.notion.com/v1/{path} with headers: Authorization Bearer token, Notion-Version: 2022-06-28, and Content-Type: application/json. For POST/PATCH/PUT pass the body as JSON. Return the Notion API response. Handle errors with descriptive messages including the Notion error code if present.

Paste this in Lovable chat

supabase/functions/notion-api/index.ts
1import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
2
3const corsHeaders = {
4 "Access-Control-Allow-Origin": "*",
5 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
6};
7
8serve(async (req) => {
9 if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
10
11 try {
12 const notionToken = Deno.env.get("NOTION_TOKEN");
13 if (!notionToken) throw new Error("NOTION_TOKEN not configured");
14
15 const { path, method = "GET", body: reqBody } = await req.json();
16 if (!path) throw new Error("path is required");
17
18 const url = `https://api.notion.com/v1/${path}`;
19
20 const fetchOptions: RequestInit = {
21 method,
22 headers: {
23 "Authorization": `Bearer ${notionToken}`,
24 "Notion-Version": "2022-06-28",
25 "Content-Type": "application/json",
26 },
27 };
28
29 if (reqBody && method !== "GET") {
30 fetchOptions.body = JSON.stringify(reqBody);
31 }
32
33 const response = await fetch(url, fetchOptions);
34 const data = await response.json();
35
36 if (!response.ok) {
37 const errorMsg = data.message || data.code || JSON.stringify(data);
38 throw new Error(`Notion API error (${response.status}): ${errorMsg}`);
39 }
40
41 return new Response(JSON.stringify(data), {
42 headers: { ...corsHeaders, "Content-Type": "application/json" },
43 });
44 } catch (error) {
45 return new Response(
46 JSON.stringify({ error: error.message }),
47 { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
48 );
49 }
50});

Pro tip: Test your Edge Function by calling it with path set to 'databases/{your-database-id}/query' and an empty body {} for the POST. If you receive a results array, the token and permissions are working correctly.

Expected result: The notion-api Edge Function is deployed and returns Notion database content when called with a valid database ID.

4

Query Notion databases and display content in Lovable

With the Edge Function working, ask Lovable to build the data fetching and display layer. The Notion database query endpoint accepts a filter object and sorts array in the request body. Filters follow a specific structure: each filter has a property name, a condition type (equals, contains, is_empty, etc.) matching the property type, and a value. For example, filtering a select property named 'Status' to equal 'Published' looks like: { filter: { property: 'Status', select: { equals: 'Published' } } }. Sorts specify a property and direction: { sorts: [{ property: 'Created time', direction: 'descending' }] }. The database query response returns page objects where each page has an id (UUID), properties object with the database schema values, and url. To display the full content of a page, make a second call to GET /blocks/{pageId}/children to retrieve the block tree. Each block has a type (paragraph, heading_1, heading_2, bulleted_list_item, code, image, etc.) and a type-specific content object. Build a simple block renderer in React that maps block types to appropriate HTML elements — paragraphs become p tags, heading_1 becomes h1, etc. This is the minimum needed to display Notion page content in your Lovable app. For complex formatting or deeply nested blocks, consider using an existing Notion renderer library.

Lovable Prompt

Build a Notion database viewer that calls my notion-api Edge Function. Use NOTION_DATABASE_ID from environment or accept a database ID as a prop. Query the database with a POST to databases/{id}/query with a filter for Status property equals 'Published' and sort by last edited time descending. Display results as cards in a grid showing the page title, any description or summary property, and the last edited date. Clicking a card fetches the page blocks from blocks/{id}/children and renders them in a detail view. Render paragraphs as text, heading_1 as h1, heading_2 as h2, and bulleted_list_item as list items.

Paste this in Lovable chat

Pro tip: Notion's block API is paginated — responses include a has_more boolean and next_cursor for fetching subsequent pages. For long documents, implement cursor-based pagination to fetch all blocks.

Expected result: A Notion database viewer displays filtered pages as cards and renders full page content as formatted text when a card is clicked.

5

Add write operations for database record creation and updates

Extend your integration with write capability to create new database records and update existing page properties from your Lovable app. Creating a new Notion database page requires a POST to /pages with a parent object specifying the database ID and a properties object matching the database schema exactly. Each property value must be formatted according to its type — a title property needs { title: [{ text: { content: 'Your title' } }] }, a select property needs { select: { name: 'Option Name' } }, and a date property needs { date: { start: '2026-03-30' } }. Updating a page uses PATCH to /pages/{pageId} with only the properties you want to change — you do not need to include unchanged properties. Ask Lovable to generate a form component that matches your database schema. Describe each property's name and type in your prompt so Lovable can generate the correct input types (dropdowns for select, date pickers for date properties, etc.) and format the API request body correctly. After a successful create or update, invalidate the cached database query results and refetch to show the latest data. Add optimistic UI updates where appropriate to make the form submission feel instantaneous even if the Notion API call takes a moment.

Lovable Prompt

Add a 'New Item' button to my Notion database viewer that opens a modal form for creating a new record. The form should have fields matching my database schema: Title (text input), Status (dropdown with Published/Draft/Review options), Description (textarea), and Due Date (date picker). On submit, call my notion-api Edge Function with a POST to pages with the parent database ID and the properly formatted properties object. After success, close the modal and refresh the database list. Also add an Edit button on each card that opens the same form pre-filled with the current values for updating via PATCH.

Paste this in Lovable chat

Pro tip: If you get property type errors when creating Notion pages, double-check the exact property names in your Notion database — names are case-sensitive and must match exactly, including spaces and special characters.

Expected result: The dashboard supports creating and updating Notion database records through form submissions that write back to Notion via the Edge Function.

Common use cases

Customer-facing knowledge base from Notion content

Build a public or gated knowledge base website in Lovable that pulls content directly from a Notion database. Teams write and publish articles in Notion, and the Lovable app displays them with search, category filtering, and table of contents navigation — turning Notion into a full CMS without any separate publishing platform.

Lovable Prompt

Create a knowledge base app that fetches articles from my Notion database via the notion-api Edge Function. Query the database with a filter for status = Published and display articles as cards in a grid with title, description excerpt, category tag, and last updated date. Add a search bar that filters articles by title. Clicking an article navigates to a detail page that fetches and renders the full Notion page blocks including headings, paragraphs, and bullet lists.

Copy this prompt to try it in Lovable

Project status dashboard from Notion database

Display a live project tracker that reads project records from a Notion database with status, owner, due date, and priority properties. Give stakeholders a read-only view of all projects without needing Notion access, with filtering by status and owner.

Lovable Prompt

Build a project status dashboard that queries my Notion projects database via the notion-api Edge Function. Fetch all pages with properties: Name, Status (select), Owner (person), Due Date (date), and Priority (select). Display projects in a kanban board grouped by Status. Show each project card with its name, owner, due date, and a priority badge color-coded by value. Add filter buttons for each owner's name.

Copy this prompt to try it in Lovable

Content calendar and publishing workflow tool

Create a content management interface that reads from a Notion content calendar database and allows content managers to update post status and publish dates. Combine read and write operations to enable a workflow where content is drafted in Notion and managed through a cleaner custom UI.

Lovable Prompt

Build a content calendar interface connected to my Notion content database via the notion-api Edge Function. Fetch all content items with their Title, Status, Publish Date, Content Type, and Assignee properties. Display them in a monthly calendar view on their publish dates. Allow clicking a content item to open a detail panel that shows the full properties. Add a status update button that sends a PATCH request through the Edge Function to update the item's Status property in Notion.

Copy this prompt to try it in Lovable

Troubleshooting

The Edge Function returns a 404 'object_not_found' error when querying a database

Cause: The database has not been shared with the Notion integration. This is the most common error when setting up Notion integrations and occurs because Notion's permission model requires each page or database to be explicitly shared.

Solution: Open Notion, navigate to the database or its parent page, click the three-dot menu or 'Share' button, find the 'Connections' section, and add your integration by name. Wait a few seconds for the change to propagate, then retry the API call. If the error persists, verify you are using the correct database ID — the UUID from the database URL, not the page title.

Database query returns pages but properties show as empty or null for most fields

Cause: The property names in your code do not exactly match the property names in the Notion database schema, which is case-sensitive and spaces-sensitive.

Solution: Call GET /databases/{id} to retrieve the full database schema including exact property names. Log the properties object from the first query result to see the exact structure and naming. Update your React component to use the exact property names from the schema rather than guessing.

Page block content fetching returns some blocks but cuts off — the page content appears truncated

Cause: Notion's blocks endpoint paginates results, returning a maximum of 100 blocks per call. Long pages with more than 100 blocks require multiple paginated calls using the next_cursor from the response.

Solution: Check the has_more field in the block children response. If true, make additional calls with start_cursor set to the next_cursor value from the previous response. Collect all blocks across multiple calls and render them together. For most use cases, 100 blocks covers the visible content — implement pagination only if your use case requires very long document rendering.

Creating a new page returns a 400 error with 'validation_error' and a property name in the message

Cause: The properties object in the POST body uses incorrect format for one or more property types, or references a property that does not exist in the database schema.

Solution: Verify the database schema by calling GET /databases/{id} and checking the properties object for each field's type definition. Match your create request format exactly to the type — title properties need the title array format, select properties need the select.name format, and rich_text properties need the rich_text array format. The Notion API reference at developers.notion.com shows the exact JSON structure for each property type.

Best practices

  • Share only the specific pages and databases your application needs with the Notion integration — the principle of least privilege applies here, and over-sharing creates unnecessary risk if the token is compromised
  • Store the Notion integration token in Cloud Secrets and never expose it in frontend code — unlike public-facing APIs, Notion integration tokens provide write access to shared pages
  • Cache Notion database query results in your Supabase database or React state for at least 5-10 minutes to avoid hitting Notion's rate limits, since Notion data updates are typically non-real-time
  • Handle the Notion-Version header carefully — always use the version you developed against (2022-06-28 as of March 2026) to avoid breaking changes when Notion releases new API versions
  • Implement pagination support for database queries and block children from the start — Notion paginates results at 100 items, and hitting this limit in production is harder to debug than building pagination in initially
  • Validate the properties format for each Notion property type before sending create/update requests — incorrect format returns generic validation errors that are harder to debug than client-side validation messages
  • Consider using Lovable's built-in Notion MCP personal connector alongside the Edge Function runtime integration — the MCP connector helps Lovable's AI generate more accurate code by reading your database schemas and page content during the build process

Alternatives

Frequently asked questions

What is the difference between Notion's MCP personal connector and the Edge Function integration in Lovable?

Notion's MCP personal connector in Lovable gives the AI agent build-time context by reading your Notion pages and databases while you are building — so if you have a PRD in Notion, the AI can read it to generate more relevant code. The Edge Function integration provides runtime capability — your deployed application can read and write Notion data while users are actually using it. You can and should use both: MCP for building, Edge Functions for deployment.

Can my Lovable app write data back to Notion, or is it read-only?

Lovable applications can both read from and write to Notion via the Edge Function integration. Write operations require the 'Insert content' and 'Update content' capabilities to be enabled on your Notion integration (configurable at notion.so/my-integrations). You can create new database records, update page properties, add blocks to pages, and delete content depending on the permissions you grant.

How do I render Notion's rich text formatting (bold, italic, links) in my Lovable React components?

Notion's rich_text objects contain an array of text chunks, each with an annotations object describing formatting (bold, italic, underline, code, strikethrough) and optional href for links. To render correctly, map each text chunk and apply the corresponding React inline element — wrap bold text in strong, italic in em, and links in a tags. The Notion block renderer pattern handles this transformation from Notion's structured format to HTML-like React elements.

What happens if my Notion database has more than 100 records — does the API return all of them?

The Notion database query endpoint returns a maximum of 100 results per call. If your database has more records, the response includes has_more: true and a next_cursor string. Make additional query calls with start_cursor set to this value to retrieve subsequent pages. Continue until has_more is false. For displaying large databases in a Lovable app, implement virtual scrolling or limit the query with date range filters rather than fetching all records at once.

Does the Notion API support searching across all pages in a workspace?

Yes, the Notion API includes a POST /search endpoint that searches across all pages and databases shared with your integration. It accepts a query string and optional filter for page_or_database type. This is useful for building a search-as-you-type feature in your knowledge base app. The search is limited to content shared with your integration and does not search page body text — only titles and database property values.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.