DynoStaticAdmin sign in
Documentation

Integration

Integrating DynoStatic

DynoStatic widgets use one dependency-free classic loader. The loader derives the installation address from its own URL, fetches the published widget by ULID, and mounts it into a matching host element.

Register the website

In Admin → Websites, choose Add website and enter the embedding website's exact origin. An origin contains only the scheme, hostname, and optional non-default port:

  • https://www.example.com
  • http://127.0.0.1:4321

Do not enter an article path. Browser security treats HTTP and HTTPS, different hostnames, and different ports as separate origins.

Local staging is supported. Register the local origin while developing. At launch, edit the website address under General. DynoStatic can replace the origin on existing discussion URLs while preserving paths, query strings, comments, reactions, and moderation state.

Additional exact origins can be added under the website's General → Website addresses screen for staging, documentation, or alternate front ends. Removing an alias immediately stops new public reads and writes from that origin; the primary address is replaced in the same screen so discussion URLs move safely.

The same Integrations screen manages optional signed HTTPS webhooks for comment, form, and CMS content events (content.published, content.unpublished, media.updated, content.schema_changed). A signing secret is shown once. Endpoints can be paused without losing delivery history, and the panel reports attempt count, latest attempt time, and latest HTTP outcome. Delivery is asynchronous: configure the optional once-per-minute worker shown under Tools → Optimization for predictable timing, or use Run background jobs now under Tools → System while testing. Use content.published webhooks to trigger Astro/Hugo host rebuilds.

Form widgets can also enable per-widget email notifications in the builder. Recipients must be valid email addresses. By default the message links to the private admin inbox and omits decrypted answers unless the operator opts in.

Headless CMS for Astro and Hugo

For marketing pages, blog posts, and site settings that your static generator should render at build time, use Content (CMS) instead of widgets. Create collections and singletons, publish entries, mint a Bearer API token, and load JSON from api/?action=cms.* during CI. Full details: cms.md and examples/astro-cms/.

Widgets remain the right tool for live comments, forms, and other interactive embeds on the published site.

Create and publish a widget

Select the website, choose Add Widget, and create a widget whose catalog status is Ready, Official embed, or Link only. Configure it, inspect the desktop/tablet/mobile preview, then publish it. The builder provides a 26-character widget identifier and copyable embed markup.

Comments use the complete discussion runtime. Forms, polls, consent, calendars, menus, and other compatible widgets use the shared runtime. Catalog entries marked Preview, Credentials required, Approval required, or Deferred cannot be published as if they were complete.

Static HTML

Place the host and loader where the widget should appear:

<div data-dynostatic-widget="WIDGET_ULID"></div>
<script data-cfasync="false" src="https://widgets.example.com/embed.js" defer></script>

For an installation in a subfolder, retain that path:

<script data-cfasync="false" src="https://example.com/tools/widgets/embed.js" defer></script>

Load embed.js once per page even when several widget hosts exist. The loader scans existing and newly inserted [data-dynostatic-widget] elements and does not mount one host twice.

An explicitly shared widget also names the exact recipient website. Keep the source widget ID and use the recipient—not the source—website ID:

<div data-dynostatic-widget="WIDGET_ULID" data-dynostatic-site="RECIPIENT_SITE_ULID"></div>

This attribute identifies an existing authorized placement; it does not create or infer sharing authority. Existing self placements omit it unchanged.

Widgets load lazily by default and activate within 600 pixels of the viewport. Add data-loading="eager" to the loader only when immediate initialization is necessary. A particular host may opt in with data-dynostatic-loading="eager".

Set data-dynostatic-mode="light" on a host to request light DOM for compatible shared widgets. Comments choose Shadow DOM or light DOM from the website's Appearance setting so host-owned themes work consistently.

Astro and view transitions

Use a host-only component for each widget and put the loader once in the shared layout. The complete copy-paste components are under examples/astro-widgets/. The resulting markup uses the same canonical primitives:

<div data-dynostatic-widget="WIDGET_ULID"></div>
<script
  is:inline
  data-cfasync="false"
  src="https://widgets.example.com/embed.js"
  defer
></script>

The loader listens for astro:before-swap and astro:page-load, releases timers and pending observers before a swap, and rescans the new page afterward.

Astro's is:inline prevents bundling; the script is still an external deferred request. CMS posts and pages use the separate private build client under examples/astro-cms/. Never put its Bearer token in a widget component or browser bundle.

Hugo

The host shortcode and once-per-layout loader partial under examples/hugo-widgets/ retain root or subfolder installation paths. Put each widget ID in a dynostatic-widget shortcode, and include the loader partial once before </body>.

Hugo CMS builds use the separate server-side partial under examples/hugo-cms/. It reads the Content API URL and Bearer token from the private build environment; neither belongs in the public widget partial or generated HTML.

WordPress

Build the separately installable plugin archive from a development checkout:

php bin/build-wordpress-connector.php /absolute/output/dynostatic-connector.zip

Upload and activate that ZIP in WordPress. Under Settings → DynoStatic, enter the public installation URL and the 26-character ID shown by the published widget. Then insert the DynoStatic Widget block or use [dynostatic_widget]. Repeating either host still enqueues only one deferred loader. The plugin stores no DynoStatic password or token and does not load panel code.

Shopify Custom Liquid

Add one Custom liquid section where the widget should appear. Paste the ordinary canonical snippet; replace only the widget ID and installation URL:

<div data-dynostatic-widget="WIDGET_ULID"></div>
<script data-cfasync="false" src="https://widgets.example.com/embed.js" defer></script>

If several sections contain widgets, keep the host in each section but retain the script only once, preferably in theme.liquid before </body>. This is a generic embed, not a Shopify app, and it does not synchronize Shopify content.

Webflow Embed

Add an Embed element where the widget should appear and paste:

<div data-dynostatic-widget="WIDGET_ULID"></div>
<script data-cfasync="false" src="https://widgets.example.com/embed.js" defer></script>

For multiple widgets, keep one host per Embed element and put the loader once in the site's Before </body> tag custom code. Webflow may require a paid site plan for custom code. This is a generic embed, not a native Webflow app.

Discussion identity

For comments, the current full page URL is sent to the discussion API and must belong to the registered origin. A deliberate stable identity may be supplied as data-page-key on the widget host. Most sites should use URL identity so discussion administration can display and update the real address.

Fragments never identify a different discussion. Website settings determine which query keys, if any, participate in page identity. Moving from a local or old origin to a live origin preserves each normalized path and allowed query.

Browser events

The shared runtime emits dynostatic:ready, dynostatic:submitted, dynostatic:consent, and dynostatic:error on window. The comments element additionally emits bubbling, composed dynostatic-comments:ready and dynostatic-comments:metadata events. Metadata is public, read-only state; it does not include email addresses, visitor credentials, administrator data, or anti-spam evidence.

Verify the integration

  1. Confirm embed.js loads from the intended installation and version.
  2. Confirm the host contains a valid published widget ULID.
  3. Confirm api/index.php?action=widget succeeds for the registered origin.
  4. For comments, confirm bootstrap and thread requests identify the intended URL or explicit page key.
  5. Check narrow widths, keyboard focus, reduced motion, light/dark themes, empty/loading/error states, and the widget's write workflow.
  6. Submit real data only with the website owner's authorization.

Never put Turnstile secrets, SMTP credentials, Redis passwords, API tokens, or administrator credentials into embed markup.