Appearance
Installation
Most website install patterns include loader.js with data-zeeker-config="..." somewhere on the page. Host-triggered integrations use the smaller interaction-first trigger.js entry point instead. Zeeker reads the saved config and adds the selected canonical Social surface in the position you chose in the configurator.
Use the same snippet across a whole site, or place it only on pages where context matters most: articles, docs, product pages, changelogs, dashboards and in-app workflows.
The embed dashboard calls this step Deploy. It starts with the platform selected during setup—web patterns for a Website embed or iPhone/Android and WebView patterns for a Mobile App embed—while keeping the other paths available for hybrid products.
For an Embed Element, the config ID selects saved ordinary controls that may have been initialized by an Element preset. Do not add an Element name to the public script tag or downstream iframe query. The published community capability and embed configuration are authoritative.
Native mobile apps
Choose Mobile App during creation, then pick a preset or custom configuration. The native SDK hosts the same Social composition used on the web in a WKWebView or Android WebView managed by ZeekerEmbedView.
For a contextual Element, pass a stable appId, objectType, and objectId so a website and mobile app can resolve the same logical article, product, event, lesson, or record. Optional title, description, image, and URL values supply display context.
The same Embed model also supports broader integration shapes:
- Full App from Menu — your native menu item, tab, or button opens the preloaded community app full screen.
- Integrated Community Screen — a fixed feed inside an app screen.
- Integrated Conversation — a fixed discussion resolved from a stable app object ID.
- Integrated Community Chat — a fixed member-chat screen.
Full-app embeds retain normal document scrolling, animated in-embed navigation, profile/filter swipes, and swipe-back navigation. A community configured with post-tag filter lanes shows those same lanes in the Embed, including a text-only All lane, full untruncated tag names in a horizontally scrollable strip when needed, horizontal swipe switching, and the member's hidden sort preference within each tag.
Minimum snippet
html
<script
src="https://embed.zeeker.com/api/embed/widget/loader.js"
data-zeeker-config="YOUR_CONFIG_ID"
async
></script>Place it once in your global layout, preferably in <head> so the small loader is discovered immediately. The script is asynchronous: it does not block HTML parsing. It installs a ~1.7 KB gzip API bootstrap, while the current ~43.5 KB gzip runtime is requested asynchronously only when the selected mode needs it.
On URL-scoped article and page discussions, the web loader reads og:title, og:description, og:image, and og:site_name from the rendered host document. The title falls back to document.title, and the loader also reads the page icon. Relative image and icon references are resolved against the page URL. The loader sends these bounded values in the JSON boot body, never in the URL.
This browser handoff matters when a publisher permits normal readers but blocks Zeeker's server-side Open Graph fetch. Zeeker accepts it only when the browser Origin, the resolved article origin, and the Embed's configured site origin match exactly; an Embed without a configured site URL uses server extraction only. Zeeker validates and bounds every value, keeps successful server extraction authoritative, and uses host values only to fill missing or generic fields. It does not invent a description or image that is absent from both sources. Existing contextual roots are enriched the next time their published Embed boots, so a previously URL-only card can recover without creating a second thread.
Use complete Open Graph tags and an absolute HTTPS og:image when possible. Zeeker still applies its publisher-resource restriction policy before storing or displaying any supplied preview field.
All script-tag attributes
The widget reads these data-* attributes from its own <script> tag:
| Attribute | Required | Purpose |
|---|---|---|
data-zeeker-config | yes | Embed config ID, copied from the configurator. |
data-zeeker-mode | no | Override container mode: floating | fixed | anchor | pill | trigger. Defaults to whatever the admin saved. |
data-zeeker-target | no | DOM id to render into (used with fixed and anchor modes). |
data-zeeker-height | no | Reserved desktop height for a fixed target. Generated snippets set this from the published layout to prevent CLS. |
data-zeeker-mobile-height | no | Reserved mobile height for the same fixed target. |
data-zeeker-selector | no | Host control selector for trigger.js. Supplying it avoids a later config lookup just to discover the trigger. |
data-zeeker-url | no | Canonical URL to scope the thread to. Defaults to window.location.href. Useful for SPAs that want a sticky URL across navigations. |
data-zeeker-normalize | no | Set to false to preserve the supplied URL verbatim. By default Zeeker removes fragments and common tracking parameters before resolving context. |
data-zeeker-thread | no | Stable custom discussion key for configs whose resolution strategy is custom. |
data-zeeker-app-id | contextual | Stable identifier for the host application. Use with object type and ID for app/object resolution. |
data-zeeker-object-type | contextual | Host object category such as article, product, lesson, or event. |
data-zeeker-object-id | contextual | Stable host-side identifier for the object. Do not use a mutable title or URL slug. |
data-zeeker-object-title | no | Display title for the host object. |
data-zeeker-object-description | no | Display description for the host object. |
data-zeeker-object-image | no | Absolute display-image URL for the host object. |
data-zeeker-object-url | no | Canonical web/deep-link URL for the host object. |
data-zeeker-object-site-name | no | Publisher/application display name (data-zeeker-app-name is a supported alias). |
data-zeeker-object-favicon | no | Absolute favicon/application icon URL. |
data-zeeker-native | no | Native host hint, for example webview, when running the web loader inside an app WebView. |
data-zeeker-theme | no | Explicit runtime color scheme override: light or dark. Omit it to use the config's auto, light, or dark setting. |
data-zeeker-radius | no | CSS length applied as --zk-radius (e.g. 8px, 0). |
data-zeeker-font | no | Font-family stack applied as --zk-font. |
data-zeeker-accent | no | Hex color applied as --zk-accent. |
data-zeeker-hide-header | no | Presentation override: 1 hides the embedded thread header. |
data-zeeker-hide-link-card | no | Presentation override: 1 hides the contextual root/link card. |
data-zeeker-show-composer | no | Presentation override: 0 hides the composer. A host cannot use it to override a server-side read-only participation policy. |
Connection, read access, participation, sign-in method/timing, outside access, content selection, container border, top header, scrolling/loading behavior, and other policy-bearing controls are intentionally absent from this table. They are server-authoritative config fields and cannot be weakened with publisher markup.
data-zeeker-preview-token, data-zeeker-source="dashboard-preview", and all data-zeeker-preview-* attributes are private dashboard-preview plumbing. Never copy them into a production install. The backend ignores preview overrides unless a config-bound signed token is valid on the API-owned preview route.
Installing an Embed-only config
Embed only uses the same public loader and config ID as Connected; do not put an isolation token or signing secret in host HTML. When the deployment supports isolation, the loader obtains a short-lived signed scope for the exact config and refreshes it while the config remains published and eligible. The scope chooses a content partition but does not replace Zeeker login, community SSO, or membership checks.
The capability is server-reported and fail-closed. If required migrations or database constraints are incomplete, or the scope-signing secret is unavailable to a participating service, publishing/booting or the affected scoped request is rejected. It never retries against ordinary Social as Connected. Editing, pausing, unpublishing, or changing the network mode advances or invalidates the live config revision, so an old scope stops working on the next protected Social or Chat request.
Named guest participation is always Embed only and appears only when the selected Social or Chat surface reports its complete exact-config boundary. Login/signup remains available alongside Continue as guest, and the guest supplies a display name. Anonymous attribution remains a separate signed-in-user feature.
Contextual Element example
This product-page example can back a review or question composition. The config selects existing allowed Social post types and presentation; the object fields identify the product:
html
<div id="product-community"></div>
<script
src="https://embed.zeeker.com/api/embed/widget/loader.js"
data-zeeker-config="YOUR_ELEMENT_CONFIG_ID"
data-zeeker-mode="fixed"
data-zeeker-target="product-community"
data-zeeker-app-id="storefront"
data-zeeker-object-type="product"
data-zeeker-object-id="sku_12345"
data-zeeker-object-title="Canvas Weekender"
data-zeeker-object-url="https://shop.example.com/products/canvas-weekender"
async
></script>Use the same three identity fields in ZeekerObject from the iOS or Android SDK. See Web and mobile Element implementations.
For discussion text in your own page's initial HTML, see Publisher-page search.
For trigger mode, use trigger.js and connect your host control with data-zeeker-selector. This is a common MOD Chat pattern: make the host control a floating support button on desktop and a chat/support item in the mobile app's own bottom tray. The same Chat Element can instead use the Anchor launcher, fixed inline, or full-screen placement.
html
<button id="open-zeeker">Open community</button>
<script
src="https://embed.zeeker.com/api/embed/widget/trigger.js"
data-zeeker-config="YOUR_CONFIG_ID"
data-zeeker-mode="trigger"
data-zeeker-selector="#open-zeeker"
async
></script>You can instead save the selector in the configurator, or call the JavaScript API after the trigger bootstrap has executed:
js
document.querySelector('#open-zeeker').addEventListener('click', () => {
window.zeeker.open();
});The configured trigger decides whether this opens the page discussion, community app, group chat, or private MOD Chat, and whether it uses a panel or full-screen presentation.
Publisher-page search
Zeeker can supply eligible public discussion replies for a visible section of the publisher's article or product page. The section becomes part of the host HTML, with ordinary links to public reply and author pages when available. Your page keeps its own title, canonical URL, robots rules, and sitemap. Search engines decide whether to index the page and how to rank it; installing an Embed does not guarantee a ranking increase or a rich result.
The async loader supplies the interactive experience. A floating panel opens after a visitor's action, so its content alone should not be relied on as the host page's crawlable discussion. Use the same visible summary with either placement:
| Placement | Publisher HTML |
|---|---|
| Fixed | Render the summary immediately before, and outside, the target <div>; the runtime owns and clears the target. |
| Floating or host trigger | Choose a visible section below the article or product. Keep the launcher installation; the summary creates no second runtime. |
| Chat or community feed | The per-page reply snapshot is for contextual discussions. Do not invent an article summary from private chat or unrelated feed content. |
The WordPress plugin renders the fixed summary automatically for an eligible contextual Element. For a contextual floating or host-triggered config, place [zeeker_discussion_snapshot] in a Shortcode block at the location readers should see it. No summary is added automatically to every page by a floating launcher. See WordPress snapshots.
For another server-rendered framework, download the server integration module into your server project and review it with your deployment code. The module has no dependencies and uses your server's standard fetch API:
js
import {
fetchDiscussionSnapshot,
renderDiscussionSnapshot,
} from './zeeker-discussion-snapshot.mjs';
// Trusted canonical page URL from your router/CMS, not a visitor query value.
const binding = {
configId: 'YOUR_PUBLISHED_CONFIG_UUID',
canonicalUrl: 'https://publisher.example/articles/your-article',
};
const snapshot = await fetchDiscussionSnapshot(binding);
const discussionHtml = renderDiscussionSnapshot(snapshot, binding);
// Insert discussionHtml in the visible server-rendered article template.
// Fixed: place it before <div id="zeeker-embed"></div>, outside that div.
// Floating: place it at your chosen article/product section.
// Keep the normal async loader snippet for the interactive discussion.This integration requests only https://embed.zeeker.com/api/embed/widget/snapshot?config=CONFIG_UUID&url=CANONICAL_URL. It never sends viewer credentials or follows redirects. A two-second timeout, bounded response, unavailable discussion, or API error yields an empty summary and leaves your page and live Embed usable. The renderer checks the response's exact page/config binding, escapes text, limits replies, and constrains all public Social anchors to https://zeeker.com/p/... or /u/.... Isolated replies have no Social post or profile links; the integration does not create them.
Snapshot publication requires the indexing launch gate, a published config, public reading, an exact configured-site/discussion origin match, a public non-NSFW community, and the correct Connected/Embed-only partition. Only approved non-anonymous replies from public active accounts, or eligible scoped guests, are supplied. Private/unlisted communities, authenticated-only content, moderation removals, deleted content, and private/deactivated authors fail closed. The endpoint is read-only: it cannot create a discussion on an article that has none yet. Its cursor retrieves another bounded page; use only content you actually display to readers. The JSON endpoint and oEmbed player remain noindex and do not belong in your publisher sitemap.
Render the same section for readers and crawlers. Do not hide it offscreen, collapse it solely for search engines, stuff unrelated text into a floating launcher, or replace the publisher's canonical with a Zeeker URL. The module does not add structured data: if your site already marks up an article or product, only add discussion fields that match the visible section and the applicable schema rules. A bounded excerpt is not an entire review collection and must not be used to invent an aggregate rating.
The module requests fresh data with cache: 'no-store', but your framework or CDN can still cache the completed host HTML. Configure a short expiry and a purge path for privacy/moderation changes; the API cannot erase an already cached publisher copy. The WordPress data cache lasts one minute, independently of any full-page cache. Disable host-page caching when immediate revocation is required. Measure the request's effect on host TTFB and check real Core Web Vitals; async loading and reserved fixed dimensions reduce interference but cannot guarantee unchanged performance on every publisher site.
Verify the publisher URL with JavaScript disabled: approved text and canonical anchors must already appear in View Source. Then check the rendered URL in Search Console's URL Inspection and run PageSpeed Insights before and after installing. Google recommends server rendering for JavaScript content, crawlable anchors, and structured data that reflects visible content.
Google's indexifembedded directive can permit embedded content to be indexed despite noindex. Zeeker does not enable it on the shared interactive shell: that shell also serves access-gated surfaces, and a blanket directive cannot establish current discussion policy. The visible publisher summary is the supported integration for this release.
SPAs
Single-page apps (React Router, Vue Router, Next.js, SvelteKit, etc.) keep the same <script> tag in their root layout — the widget is bootstrapped once and then participates in client-side navigations:
- For each route change, call
window.zeeker.refresh()(it reloads the widget for the new URL). - Or, if you control the canonical URL, set
data-zeeker-urlto a stable value (e.g. the article slug) so navigations within that route don't change the thread.
jsx
// React example
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export default function ZeekerWidgetRefresh() {
const loc = useLocation();
useEffect(() => {
window.zeeker?.refresh();
}, [loc.pathname]);
return null;
}Next.js
Use a native asynchronous script in the initial root-layout markup:
tsx
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<head>
<script
async
src="https://embed.zeeker.com/api/embed/widget/loader.js"
data-zeeker-config={process.env.NEXT_PUBLIC_ZEEKER_CONFIG_ID}
/>
</head>
<body>
{children}
</body>
</html>
);
}The browser discovers the loader with the initial HTML, downloads it in parallel, and executes it as soon as it is available without waiting for hydration.
WordPress
The official Anchor.Social 0.5.0 plugin is the recommended path for WordPress Embed Elements:
- Install and activate the plugin.
- Name the community and choose a Group Chat, MOD Chat, Support Forum, Comments, Product Reviews, Product Q&A, Community Feed, or Polls starting preset.
- Choose automatic article/product placement, the dynamic Anchor Community block (
zeeker/social-element) /[zeeker_element]shortcode, a focused launcher, a host trigger, or full-screen opening. Group Chat and MOD Chat default to the desktop launcher/mobile tray but can use any published placement. - Set Auto/Light/Dark theme, view/participation access, and Zeeker versus Zeeker/community-SSO login. First-run sign-in starts on a gated action.
- For Comments or a compatible legacy discussion config, optionally migrate published posts and approved comments.
The plugin provisions an unlisted, ownerless community and publishes the preset's ordinary Social/Embed configuration. It assigns no temporary user or owner. No Zeeker account, config ID, or pasted loader is required. Dashboard signup later claims that already-live config and only then assigns the user as owner.
Download the WordPress plugin · Complete WordPress guide · Plugin details
Preset-created compositions preserve their selected Social/Chat surface: discussions and polls can follow singular posts; existing review/question post types can mount on singular WooCommerce products and receive stable product ID, title, description, image, and canonical URL context; feed or explicitly fixed Chat compositions can live on a deliberate block or shortcode page; and launcher/trigger/full-screen configs remain activation based. WordPress preserves the Widget's published placement. One page boots one Zeeker runtime until multi-instance hosting is supported.
The plugin uses the actual async Zeeker loader. Content selection and policy come from the published community/embed configuration; a block, shortcode, or data attribute cannot enable a disallowed Social capability or weaken authentication. The plugin can also connect a published Widget ID created in the Embed dashboard.
SSO provider configuration remains community-level. Account-free WordPress setup starts in Connected mode with gated-action login; after claim and SSO setup, the Embed dashboard can select community-SSO-only, automatic, or host-managed identity. It can also select Embed only and named guests when that deployment reports the applicable capabilities. Missing isolation or guest prerequisites fail closed rather than weakening the saved policy.
Archive migration remains type-safe: published posts and approved comments can populate compatible ordinary discussions, but the plugin does not fabricate ratings, product questions, chat messages, support topics, or poll votes from WordPress comments.
Activation itself makes no external request. Explicit setup records consent before sending the public site metadata, starting preset/theme/supported policy, WordPress/plugin versions, locale, and random installation credentials to Zeeker.
For a manual install, paste the snippet into Appearance → Theme File Editor → footer.php just before </body>. You can also use a header/footer scripts plugin and add the snippet to its footer scripts box.
If you don't want the widget on every URL, wrap it in a conditional template tag — e.g. is_singular('post') to limit it to blog posts.
Webflow
In Project Settings → Custom Code → Footer Code, paste the snippet. Publish. The widget appears on every page; use the data-zeeker-mode override on individual pages via the page-level custom code box if you want a different mode for a section.
Shopify
In Online Store → Themes → Edit code, open layout/theme.liquid and paste the snippet just before </body>. The same data-zeeker-url override pattern applies to product pages so ?variant= query params don't fork the thread.
Framer
Add a Custom Code override under Site Settings → Custom Code → End of <body>.
Plain static HTML
Paste the snippet into your template. Done.
Iframes & sandboxed contexts
The bootstrap runs in the host page and opens Zeeker UI in a child iframe only when the selected mode needs one. If your CSP blocks Zeeker's scripts or frames, add the relevant origins to your policy:
script-src https://embed.zeeker.com;
frame-src https://*.zeeker.com;
connect-src https://api.zeeker.com https://embed.zeeker.com;Verifying install
After publishing, open your page and check:
loader.js(ortrigger.js) succeeds in the Network panel andwindow.zeekerexists in the console.- With
loader.js,window.zeeker.getState()reports{ loaded: false, pending: ... }before the mode-gated async runtime starts; that is expected. - With
loader.js,runtime.min.jsis injected as an async script after host load/idle for floating mode or when a fixed target approaches the viewport. The configured visible mode then mounts and emitsready. - With
trigger.js, onlyopen()andclose()are installed initially. No Zeeker surface or full runtime should load until the configured host control is activated.
If any of those fail, see Troubleshooting.