In brief

The useful answer

For a self-contained Notion widget, upload an .html or .htm file, attach it to an embed with the official embed.file_upload field, and let Notion render it as an interactive HTML block in a sandboxed iframe. This can remove a separate widget host from the serving path, but it does not remove testing for signed URL expiry, network access, authentication, storage or client differences. When Notion data must refresh the widget, add a separate webhook-to-backend flow.

  • A self-contained widget can use an uploaded HTML block instead of a third-party widget host.
  • The official API sequence is upload .html or .htm, then attach it with embed.file_upload.
  • The HTML runs in a sandboxed iframe and file-backed embed URLs are temporary and signed.
  • Use a stable HTTPS URL embed for an existing hosted widget, then test UI and API differences.
  • Use a separate webhook, backend and refresh path when live Notion data must update the widget.

Can a Notion widget avoid third-party hosting?

Yes, when the widget is self-contained. Notion's July 1, 2026 release notes describe interactive HTML blocks for examples such as ROI calculators, quizzes and visualisations. The Block API reference explains that an uploaded HTML file can be rendered interactively inside Notion.

The useful distinction is between the widget's interface and its data. A self-contained calculator or quiz can potentially live as an uploaded HTML block, so a separate service does not have to serve the widget page. A tool that depends on changing private Notion data still needs a data flow, credentials boundary and refresh strategy. Native HTML can replace third-party widget hosting for the first case; it does not make the second case disappear.

The native HTML block sequence

The official API route has a short, explicit sequence:

The API example uses an upload ID nested under the embed's `file_upload` object. Use `embed.file_upload` in technical examples; `embed.fileupload` is useful as discovery wording, but it is not the official field spelling. When you create or update the embed, provide either a URL or a file upload, not both. An embed that is read back through the API returns a URL rather than the original `file_upload` parameter. These details are documented in the Notion Block API.

For a maker working in the Notion UI, the same idea is simpler: prepare the HTML file, add it as an HTML block, and confirm that the resulting interaction works in the clients your audience uses. The API sequence matters when you are generating blocks, updating them programmatically or building a repeatable publishing workflow.

  • Upload a file with an `.html` or `.htm` extension through Notion's File Upload API.
  • Create or update an embed block and attach the upload through `embed.file_upload`.
  • Let Notion render the uploaded file as an interactive HTML block.

What native HTML is a good fit for

Native HTML is most promising when the widget can do its work from its own markup, styles, scripts and user inputs. Good candidates include:

This is a fit test, not a promise that every browser feature will behave identically inside Notion. Keep the first version small and observable: show a useful empty state, explain what the inputs mean, and make failure visible instead of leaving a blank frame.

  • Calculators whose inputs and formulas are contained in the file.
  • Quizzes that score answers locally and show a result.
  • Visualisations driven by data bundled with the HTML.
  • Small interactive tools that do not need a private server on every interaction.

What “replace hosting” does not mean

The HTML file being attached to Notion does not turn the block into an unrestricted web page. The Block API says that Notion renders an uploaded HTML block in a sandboxed iframe. Treat the iframe boundary as part of the product design: do not assume direct communication with the surrounding Notion page, access to page context, or unrestricted browser capabilities.

There is also a lifecycle detail that matters for API consumers. Notion returns a temporary, signed `embed.url` for a file-backed embed. The URL expires, so a service should fetch the block again for a fresh URL rather than cache that returned link as a permanent asset URL. This is a reason to keep the Notion block ID and refresh logic in the integration layer.

Network requests, authentication, browser storage, responsive sizing and web, desktop and mobile behavior remain validation requirements. The official documentation establishes the sandbox and URL lifecycle; it does not validate the behavior of your particular HTML, dependencies or client combination. Test those conditions with the actual widget before promising compatibility.

When the widget needs live Notion data

If a change in a Notion page or database must update the widget, add a separate synchronization path. A practical architecture is Notion change → webhook → backend → fetch current data → widget refresh. The HTML block remains the rendering surface; the webhook and backend handle freshness and protected access.

Notion's webhook documentation requires a secure, publicly available endpoint for subscriptions and describes a verification step. It also says that some events, such as `page.content_updated`, can be aggregated and may arrive within a minute in its example flow. Design the widget around eventual refresh rather than assuming that every keystroke becomes an immediate event.

For production, validate webhook requests. Notion documents an `X-Notion-Signature` header containing an HMAC-SHA256 signature based on the verification token. Keep that token on the server, compare signatures safely, and ignore untrusted payloads before fetching or publishing data. Never place a Notion integration secret in the uploaded HTML sent to a browser.

Choose native HTML, a URL embed or a hybrid

Use the following decision guide:

For URL embeds created by automation, check the result in the Notion UI. The Block API notes that the Notion app uses iFramely to request URL metadata, while API-created embeds do not make that same request and may not look exactly like UI-created counterparts.

  • Native HTML block: Choose this for a self-contained calculator, quiz, visualisation or small tool. It gives the widget a native Notion location and can remove a separate serving host, subject to sandbox and client testing.
  • HTTPS URL embed: Choose this when the widget already runs on a stable host or needs server-rendered behavior. Notion's embed guidance explains the URL-based route and warns that external-login embeds do not work in the Notion desktop or mobile apps.
  • Webhook-backed widget: Add this when the widget must react to Notion changes. It is a supporting data architecture, not a replacement for the HTML-block or URL-embed rendering choice.

Test the block before calling it production-ready

Run a small compatibility pass against the real page, workspace and clients. At minimum, check:

Do not treat a successful first render as evidence that the widget is portable. The most important limitations are the ones that appear only after a reload, an expired URL, a client change or a data refresh.

  • The `.html` or `.htm` upload attaches successfully and the block renders after reopening the page.
  • The widget still works when the file-backed signed URL has expired and the integration fetches a fresh URL.
  • Network requests, third-party assets, authentication and storage either work as intended or fail with a useful message.
  • The layout remains usable at the Notion block's available width and on web, desktop and mobile clients.
  • A blocked request, missing input, stale dataset or failed refresh produces an understandable state.
  • A content update can be re-uploaded or replaced without leaving readers on an obsolete version.

The practical answer

Start with native HTML when the widget is self-contained. Upload the file, attach it with the official `embed.file_upload` field, and design for a sandboxed iframe. This is the shortest route to a Notion widget without a third-party hosting dependency.

Move to an HTTPS URL embed when an existing hosted service is the right home, and add a verified webhook-to-backend flow when Notion data must reach the interface. Before publication, re-check Notion's current release notes and API behavior, then run the compatibility tests above. That keeps the architecture matched to the widget's real needs instead of treating every interactive block as the same problem.

Evidence and further reading

Sources

  1. Notion 3.6: External Agents, HTML blocks, and moreNotion
  2. Block API referenceNotion Developers
  3. Embeds, bookmarks & link mentionsNotion Help
  4. WebhooksNotion Developers
More from the journal Contact MBS Digital