← Back to Docs

Embed Script Options

All configuration through data-* attributes. The script loads with async so it does not block page render.

<div id="threadline-comments"></div>
<script src="https://threadline.io/api/embed"
  data-site-id="YOUR_SITE_ID"
  data-api-url="https://api.threadline.io"
  async></script>

Attributes Reference

AttributeTypeDescription
data-site-idRequiredstringYour 64-character Site ID (API key) from the dashboard. Required.
data-api-urlRequiredstringComments API base URL (e.g. https://api.threadline.io). Required when the embed script is loaded from a different origin than the API.
data-page-urlstringStable page URL for the thread. Defaults to the current page URL without query/hash.
data-page-titlestringOverride the page title shown in the dashboard. Defaults to document.title.
data-theme"light" | "dark" | "auto"Force a color scheme. Defaults to "auto" (respects prefers-color-scheme).
data-languagestringISO 639-1 language code. Defaults to browser language. Supports: en, es, fr, de, pt, ja.
data-sort"latest" | "newest" | "oldest"Default comment sort. Latest ranks best comments first. Visitors can change this. Defaults to latest.
data-app-urlstringApp base for config fetches. Use a same-origin path (e.g. /threadline) for CSP-proof first-party installs.
data-auth-urlstringThreadline app origin for login popups. Keep the real Threadline URL even when using first-party rewrites.
data-max-depthnumberMaximum nesting depth for reply threads. Defaults to 6. Set to 1 for flat comments.

Plain HTML

Paste near the bottom of the page. Works on desktop and mobile browsers.

<div id="threadline-comments"></div>
<script src="https://threadline.io/api/embed"
  data-site-id="YOUR_SITE_ID"
  data-api-url="https://api.threadline.io"
  async></script>

WordPress

Add the footer hook in functions.php and place the comments container in your post template. A dedicated plugin is planned.

<?php
// Add to your theme's functions.php — loads the async embed on posts/pages.
add_action('wp_footer', function () {
  if (is_single() || is_page()) {
    echo '<script src="https://threadline.io/api/embed"
      data-site-id="YOUR_SITE_ID"
      data-api-url="https://api.threadline.io"
      async></script>';
  }
});

// In single.php / page.php (or comments.php), place the mount BEFORE the footer script:
echo '<div id="threadline-comments"></div>';

Ghost

Inject the script via Settings → Code Injection → Site Footer, and put the container in post.hbs.

{{!-- Mount in post template; script can live in Site Footer --}}
<div id="threadline-comments"></div>
<script
  src="https://threadline.io/api/embed"
  data-site-id="YOUR_SITE_ID"
  data-api-url="https://api.threadline.io"
  async></script>

Astro

Use a client-side .astro component so the loader runs after HTML is sent.

---
// src/components/ThreadlineComments.astro
---
<div id="threadline-comments"></div>

<script>
  // Runs client-side only — async so it does not block page render
  const script = document.createElement('script');
  script.src = 'https://threadline.io/api/embed';
  script.async = true;
  script.dataset.siteId = 'YOUR_SITE_ID';
  script.dataset.apiUrl = 'https://api.threadline.io';
  document.body.appendChild(script);
</script>

<!-- In your layout or post page: -->
<!-- import ThreadlineComments from '../components/ThreadlineComments.astro'; -->
<!-- <ThreadlineComments /> -->

Next.js Example

'use client';
import Script from 'next/script';

export default function ThreadlineComments() {
  return (
    <>
      <div id="threadline-comments" />
      <Script
        src="https://threadline.io/api/embed"
        data-site-id="YOUR_SITE_ID"
        data-api-url="https://api.threadline.io"
        strategy="afterInteractive"
      />
    </>
  );
}

React & Next.js integration guide

Prefer a small client component that injects the embed script once. Use env vars for site ID and API URL.

Environment variables

# .env.local
NEXT_PUBLIC_THREADLINE_SITE_ID=your_64_char_site_id
NEXT_PUBLIC_THREADLINE_API_URL=https://api.threadline.io

React (createElement + cleanup)

Creates the script with data-site-id / data-api-url, and clears window.__threadline__ on unmount so remounts can re-init.

'use client';
import { useEffect, useRef } from 'react';

export default function ThreadlineComments() {
  const mounted = useRef(false);

  useEffect(() => {
    if (mounted.current) return; // prevent double-mount in Strict Mode
    mounted.current = true;

    const script = document.createElement('script');
    script.src = 'https://threadline.io/api/embed';
    script.async = true;
    script.dataset.siteId = process.env.NEXT_PUBLIC_THREADLINE_SITE_ID!;
    script.dataset.apiUrl = process.env.NEXT_PUBLIC_THREADLINE_API_URL!;
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
      // Allow widget re-init on remount
      delete (window as any).__threadline__;
    };
  }, []);

  return <div id="threadline-comments" />;
}

Next.js (next/script)

Use strategy="afterInteractive" so the widget loads after hydration without blocking first paint.

'use client';
import Script from 'next/script';

export default function ThreadlineComments() {
  return (
    <>
      <Script
        src="https://threadline.io/api/embed"
        data-site-id={process.env.NEXT_PUBLIC_THREADLINE_SITE_ID}
        data-api-url={process.env.NEXT_PUBLIC_THREADLINE_API_URL}
        strategy="afterInteractive"
      />
      <div id="threadline-comments" />
    </>
  );
}

Tips

Static React snippet (hardcoded IDs)
'use client';
import { useEffect } from 'react';

export default function ThreadlineComments() {
  useEffect(() => {
    // Idempotent: don't inject a second copy if the bundle is already on the page.
    if (document.querySelector('script[data-tl-embed="1"]')) {
      document.dispatchEvent(new Event('threadline:remount'));
      return;
    }
    const script = document.createElement('script');
    script.src = 'https://threadline.io/api/embed';
    script.async = true;
    script.dataset.tlEmbed = '1';
    script.dataset.siteId = 'YOUR_SITE_ID';
    script.dataset.apiUrl = 'https://api.threadline.io';
    document.body.appendChild(script);
  }, []);

  return <div id="threadline-comments" />;
}

Caching & CORS