Component Tiers Reference
SpiderPublish components support 4 interactivity tiers. Each tier adds capability on top of the previous one. The tier is determined automatically by which fields are present — there is no explicit "tier" field.
Comparison Table
| Tier 1: Static | Tier 2: Interactive | Tier 3: Rich | Tier 4: App | |
|---|---|---|---|---|
| Fields | html_template + css | + js | + dependencies | + framework + source_code |
| JavaScript | None | Scoped vanilla JS | Vanilla + CDN libraries | Framework (React/Vue/Svelte) |
| CDN Libraries | No | No | Yes (allowlisted) | Optional |
| Build Step | None | None | None | esbuild (async) |
| Rendering | Liquid → Shadow DOM | + hydration script | + <script> in <head> | <script type="module"> + custom element |
| Performance | Fastest | Fast | Fast (async CDN) | Slightly heavier (JS bundle) |
| Best For | Heroes, footers, content sections | Accordions, tabs, counters, toggles | Scroll animations, carousels, charts, 3D | Dashboards, configurators, complex forms |
Which Tier Should I Use?
Rule of thumb: Start with the lowest tier that meets your needs. Tier 1 is the fastest and simplest. Only move up when you genuinely need the extra capability.
Tier 1: Static
What: HTML template (Liquid) + CSS. No JavaScript execution.
Fields used:
html_template(required) — Liquid template with{{ props.* }}and{% for %}tagscss(optional) — Component CSS, scoped via Shadow DOM
How it renders:
- Liquid engine processes
html_templatewith merged props - Output wrapped in
<spideriq-cmp data-slug="...">with Declarative Shadow DOM - CSS injected inside
<style>within the shadow root - Theme variables (
:host { --primary: #...; }) auto-injected
Output HTML:
<spideriq-cmp data-slug="hero-gradient">
<template shadowrootmode="open">
<style>
:host { --primary: #2563eb; --font-body: system-ui, sans-serif; }
.hero { background: linear-gradient(135deg, var(--primary), #000); /* ... */ }
</style>
<section class="hero">
<h1>Ship Faster with AI</h1>
</section>
</template>
</spideriq-cmp>
Limitations:
- No interactivity (no click handlers, no state changes)
- No animations beyond CSS transitions/keyframes
When to use: Content sections, heroes, footers, feature grids, pricing tables, testimonial displays, stat bars — anything that doesn't need JS.
Tier 2: Interactive
What: Tier 1 + scoped vanilla JavaScript.
Added field:
js— Vanilla JS string. Executed vianew Function('root','props', code)(shadowRoot, mergedProps)
How it renders:
- Same as Tier 1 (Liquid → Shadow DOM)
- JS stored as
<script type="spideriq/component-js">inside the shadow template (non-executable type) data-has-js="true"attribute added to<spideriq-cmp>- A ~200-byte hydration script is injected once before
</body>(shared by all Tier 2+ components on the page) - Hydration script finds all
data-has-jscomponents, reads the stored JS, and executes it scoped to each shadow root
JS environment:
root— theShadowRootelement. Useroot.querySelector()to find elementsprops— merged props object (block props + defaults)- No access to
document.body,window.location, or any parent DOM - No
importstatements — vanilla JS only console.log,setTimeout,fetchare available
Example (click counter):
// js field value:
const btn = root.querySelector('.counter-btn');
const display = root.querySelector('.count');
let count = props.start || 0;
display.textContent = count;
btn.addEventListener('click', () => {
count++;
display.textContent = count;
});
Limitations:
- No external libraries (use Tier 3 for that)
- No ES module imports
- Must be self-contained vanilla JS
When to use: Accordions, tabs, toggle switches, copy-to-clipboard buttons, counters, form validation, dark mode toggles — anything achievable with vanilla JS event listeners and DOM manipulation.
Tier 3: Rich
What: Tier 2 + CDN library dependencies from an admin-managed allowlist.
Added field:
dependencies— Array of allowlist keys (e.g.,["gsap", "gsap/ScrollTrigger"])
How it renders:
- Same as Tier 2
- Dependencies resolved from
content_cdn_allowlisttable - Library
<script>tags injected in page<head>withasync/deferper library'sload_strategy - SRI hashes applied when available
- Deduplicated: If 3 components on the same page all use
gsap, it's loaded only once
Validation:
- On component create/update, all dependency keys are validated against the allowlist
- Unknown keys are rejected with a clear error message
- Disabled libraries (
is_active = false) are also rejected
Example (GSAP scroll reveal):
{
"dependencies": ["gsap", "gsap/ScrollTrigger"],
"js": "gsap.registerPlugin(ScrollTrigger); gsap.from(root.querySelector('.content'), { opacity: 0, y: 50, duration: 1, scrollTrigger: { trigger: root.querySelector('.content'), start: 'top 80%' } });"
}
Limitations:
- Only allowlisted CDN libraries — no arbitrary
<script>URLs - Libraries are global (loaded in
<head>), but your JS still runs scoped to the shadow root - Contact your admin to add new libraries to the allowlist
When to use: Scroll-triggered animations (GSAP), carousels (Swiper), data visualizations (Chart.js), After Effects animations (Lottie), 3D scenes (Three.js), animated counters (CountUp), reactive UI (Alpine.js).
Tier 4: App
What: Full framework component (React, Vue, or Svelte). Source code is compiled server-side by esbuild into a self-contained Web Component bundle.
Added fields:
framework(required) —"react","vue", or"svelte"source_code(required) — The framework source code
Read-only fields (set by the build system):
bundle_url— R2 CDN URL of the compiled bundle (set on successful build)build_status—"none","building","success", or"failed"build_error— Error message if build failed
How it renders:
- On publish: esbuild compiles
source_codeinto a Web Component wrapper - Bundle uploaded to R2:
components/{client_id}/{slug}/v{version}/bundle.js - On the live page:
<script type="module" src="{bundle_url}">in<head> - Custom element
<spideriq-app-{slug}>renders withdata-propsattribute - The framework wrapper handles shadow root attachment, rendering, and cleanup
Build lifecycle:
POST .../publish → 202 Accepted
→ build_status: "building"
→ esbuild compiles source_code
→ Success: build_status: "success", bundle_url set
→ Failure: build_status: "failed", build_error set
Source code format:
| Framework | Format | Default Export |
|---|---|---|
| React | JSX (React 18+) | export default function Name(props) { ... } |
| Vue | SFC (.vue format) | <template>, <script setup>, <style scoped> |
| Svelte | Svelte component | <script>, HTML, <style> |
Important: When framework is set, html_template and css are ignored — the framework source is the single source of truth.
API endpoints specific to Tier 4:
GET /dashboard/content/components/{id}/build-status— Poll build progressPOST /dashboard/content/components/{id}/rebuild— Re-trigger build after fixing source (returns 202)
Limitations:
- Builds are async — publish returns 202, not 200
- Larger bundles than Tier 1-3 (framework runtime included)
- Must poll build-status before the component is usable
- Source code must be self-contained (no relative imports from other files)
When to use: Interactive dashboards, product configurators, multi-step forms, data-heavy UIs with complex state, anything requiring React/Vue/Svelte ecosystem features.
CDN Allowlist Reference
The CDN allowlist is managed by the SpiderIQ admin. These are the currently available libraries:
| Key | Name | Category | URL | Description |
|---|---|---|---|---|
gsap | GSAP Core | animation | cdn.jsdelivr.net/npm/gsap@3.12/dist/gsap.min.js | GreenSock animation library |
gsap/ScrollTrigger | GSAP ScrollTrigger | animation | cdn.jsdelivr.net/npm/gsap@3.12/dist/ScrollTrigger.min.js | Scroll-triggered animations |
gsap/Flip | GSAP Flip | animation | cdn.jsdelivr.net/npm/gsap@3.12/dist/Flip.min.js | Layout transition animations |
animejs | anime.js | animation | cdn.jsdelivr.net/npm/animejs@3.2/lib/anime.min.js | Lightweight animation library |
alpinejs | Alpine.js | framework | cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js | Minimal reactive framework |
chartjs | Chart.js | visualization | cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js | Canvas charting library |
lottie | Lottie Web | animation | cdn.jsdelivr.net/npm/lottie-web@5/build/player/lottie_light.min.js | After Effects animation player |
swiper | Swiper | carousel | cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js | Touch slider/carousel |
countup | CountUp.js | animation | cdn.jsdelivr.net/npm/countup.js@2/dist/countUp.umd.js | Animated number counter |
three | Three.js | 3d | cdn.jsdelivr.net/npm/three@0.162/build/three.module.min.js | WebGL 3D library |
API Endpoints
# List all available libraries (public, no auth)
GET /api/v1/content/cdn-allowlist
# Admin CRUD (requires auth)
POST /api/v1/dashboard/content/cdn-allowlist # Add library
PATCH /api/v1/dashboard/content/cdn-allowlist/{id} # Update
DELETE /api/v1/dashboard/content/cdn-allowlist/{id} # Remove
Contact your SpiderIQ admin to add it to the allowlist. Libraries must be hosted on trusted CDNs (jsdelivr, cdnjs, unpkg) and ideally have SRI hashes for security.
Security Model
SpiderPublish components are designed to be safe even when built by untrusted AI agents or third parties.
Shadow DOM Isolation
- Every component renders inside a Declarative Shadow DOM boundary
- Component CSS cannot leak to the parent page or other components
- Parent page CSS cannot affect component internals
- CSS custom properties (
var(--primary)) are the only intentional bridge — they pierce Shadow DOM by design
JavaScript Scoping (Tier 2-3)
- JS executes via
new Function('root','props', code)— scoped to the shadow root rootis theShadowRootelement, notdocument- No access to
document.body,window.location, cookies, or localStorage from within the function scope - Each component instance gets its own execution context
CDN Allowlist (Tier 3)
- Only admin-approved libraries from trusted CDNs can be loaded
- Unknown dependency keys are rejected at create/update time
- SRI (Subresource Integrity) hashes are enforced when available
- Disabled libraries (
is_active = false) are blocked
Framework Builds (Tier 4)
- Source code is compiled server-side by esbuild — no arbitrary code execution in the browser at build time
- Bundles are served from your own R2 CDN bucket
- The Web Component wrapper provides shadow root isolation for framework output
- No external network requests during the build process