Skip to main content

Custom HTML block

The Custom HTML block lets you paste arbitrary HTML into a post or page. The HTML is rendered as-is at publish time — no sanitisation by default, no transformation. It's a power-user escape hatch.

The block is provided by the flexweg-blocks must-use plugin (always-on).

When to use it

  • Third-party embeds that aren't provided by the built-in embed providers (e.g. Instagram, TikTok, CodePen, JSFiddle)
  • Custom widgets — Stripe checkout buttons, Calendly inline embed, Mailchimp signup forms
  • Override default layout for one specific post (e.g. inserting a custom diagram, a magazine-style pull-quote)
  • Microdata / Schema.org that the theme doesn't emit by default
  • iframes to other sites you want to embed

For commonly-needed HTML patterns (videos, tweets, multi-column layouts), use the dedicated blocks instead — they're easier to edit and more accessible.

Inserting

/custom html in the inserter, or /html. The block inserts empty.

Editing

Click the block — the right sidebar's Block tab opens with a CodeMirror editor (syntax-highlighted, line numbers). Type or paste your HTML.

The CodeMirror editor supports:

  • Full keyboard shortcuts (Cmd+A select all, Cmd+/ comment, Cmd+F find)
  • Cmd+Enter for fullscreen mode — opens a modal with the editor at full window size for working on long blobs
  • Bracket matching, auto-indent, tag matching

The Custom HTML block displays a rendered preview in the editor (not just the source). So you see the visual result inline as you edit — handy for tuning styles.

What gets rendered

Whatever you paste. No sanitisation<script> tags work, <iframe> works, <style> works, custom CSS classes work.

This is a deliberate power-user choice. The publisher's standard markdown rendering does sanitise via DOMPurify (strips <script>, dangerous attributes, etc.), but Custom HTML blocks bypass that — they're treated as opaque HTML strings.

Security implications

Because the HTML isn't sanitised:

  • A malicious <script> in a Custom HTML block runs on every visitor's browser
  • An XSS-vulnerable third-party embed can steal cookies, redirect to phishing pages, etc.
  • A self-XSS (you paste something an attacker tricked you into pasting) compromises your site

Only paste HTML from sources you trust:

  • ✓ Official embed code copied from a service's "share" UI (YouTube, Stripe, Calendly, …)
  • ✓ HTML you wrote yourself
  • ✗ HTML someone DM'd you "just paste this in your CMS"
  • ✗ HTML from a forum / random web page without auditing first

If you use the CMS to host comments or guest contributions where third parties can paste HTML — restrict the Custom HTML block via plugin (write a post.html.body filter that strips <script> from custom-html blocks unless the post's author is admin).

Markdown round-trip

The Custom HTML block is stored in the post's markdown as:

<div data-cms-block="core/custom-html" data-attrs="<base64-encoded-{html: '...'}>"></div>

The HTML you typed is base64-encoded inside data-attrs to survive markdown processing (otherwise tiptap-markdown might mangle it during round-trip). At publish time, the publisher's filter replaces the marker with the decoded HTML.

Common use cases

Embed code from a service

Most services (Stripe, Calendly, Mailchimp, etc.) provide a snippet:

<!-- Calendly inline widget -->
<div class="calendly-inline-widget" data-url="https://calendly.com/..." style="min-width:320px;height:700px;"></div>
<script type="text/javascript" src="https://assets.calendly.com/assets/external/widget.js" async></script>

Paste it into the Custom HTML block. Both the <div> and the <script> are preserved at publish.

Custom CSS (inline style)

You can scope a CSS override to one post:

<style>
.pricing-table { border: 1px solid #ccc; }
.pricing-table td { padding: 12px; }
</style>
<table class="pricing-table">

</table>

The <style> is local to the page (only this post has the rules).

For site-wide CSS overrides, use flexweg-custom-code instead — it injects to every page.

Iframe to an external site

<iframe src="https://my-tool.example.com/preview" width="100%" height="600" frameborder="0"></iframe>

Browsers respect frame-ancestors / X-Frame-Options on the embedded site.

What you can't do

  • Server-side includes (SSI) — the public site is static. No <!--#include--> etc.
  • PHP / JS runtime evaluation — no PHP runs on Flexweg. Client-side JS works (it runs in the visitor's browser), but anything server-rendered must be done by the embed provider.
  • Reference local files via PHP-style paths — e.g. <img src="<?php echo bloginfo('template_directory'); ?>/foo.png"> doesn't work. Use absolute URLs or relative paths from the published page's location.
  • JavaScript that reads admin / user data — your visitors don't have admin sessions; trying to call Firebase from the public site fails (no auth).

Continue