Skip to content

Fallback content and slots

What you write inside a component’s tag is plain HTML of your own page (the light DOM). Taggor uses it two ways:

  • Fallback: what shows while the component has not loaded, or if it never loads.
  • Slots: pieces the component shows inside itself, in the place it declared (slot="title").
Situation What appears
Before the component loads (:not(:defined)) All the HTML inside the tag, as on any page
The component loaded The component; children with slot="name" appear inside it, in the slot’s place; children without slot disappear
The Taggor script is blocked (ad blocker, the page’s CSP, the CDN is down) or the contract version is not one the runtime mounts The element is never activated: all the HTML inside stays visible and working
The component threw an error The children without slot (the fallback); slot content is hidden; the rest of the page is untouched
A browser without JavaScript All the HTML inside the tag, and what is in <noscript>
<style>
/* Before the component loads: the size it will take, so the page does not jump. */
finance-simulator:not(:defined) {
display: block;
min-height: 420px;
}
</style>
<finance-simulator rate="1.4">
<span slot="title">Simulate now</span>
<a href="https://example.com/simulator">Simulate on our site</a>
<noscript>Turn on JavaScript to simulate here.</noscript>
</finance-simulator>
  • The fallback is the child without slot (the link). It has to work on its own: a link to the same function on your site, a phone number, an e-mail. It is visible before the load, when Taggor does not arrive and when the component breaks, and it goes away when the component shows.
  • slot="title" works only for the slot names the component declares in slots (see the component’s documentation). The content appears inside the component, and is hidden if the component breaks.
  • :not(:defined) holds only until the tag is activated. Use it to give the element its final size (the component’s size: minHeight, maxWidth), so swapping the fallback for the component does not push the rest of the page.
  • <noscript> is only for what makes sense without JavaScript, such as a notice. With JavaScript on, the browser never shows <noscript>, not even when the Taggor script is blocked.
  • Never put the only fallback inside <noscript>. The most common reason a component does not load is JavaScript on and the script blocked; then <noscript> is hidden and the visitor sees nothing.
  • Never hide the fallback with :not(:defined) and no time limit (visibility: hidden, display: none, zero opacity). If the Taggor script never arrives, the tag is never activated and the content stays hidden for good. A hold that ends by itself, as in Load Taggor early, is safe.
  • If the component declares the default slot, children without slot appear inside it, and the fallback would no longer go away. Components that expect a fallback do not declare default; one that does says so in its own documentation.
  • Slot content stays yours. The component shows your element itself, not a copy: its events (addEventListener), a field’s state and your CSS keep working. The component never reads or rewrites that HTML.
  • Search and accessibility: the fallback is document HTML, which search engines read without running JavaScript. Once the component shows, the fallback leaves the screen and the accessibility tree, so screen readers do not read both.

For whoever writes the component (the customer’s agent)

Section titled “For whoever writes the component (the customer’s agent)”
  • Declare each slot in slots of defineTaggorComponent and render a native <slot name="…"> where the content should appear. Never rebuild the light DOM content (innerHTML, textContent, cloning nodes): a copy loses the page’s events and, with innerHTML, opens XSS (Taggor’s lint blocks the HTML sinks).
  • useSlot("title").hasContent says whether the page filled the slot and follows changes, so you can show a default title only when the slot is empty. For plain default text, the content inside the <slot name="title">Default title</slot> itself also works (the browser’s native fallback).
  • Do not declare default if the page should be able to write a fallback.

This example declares one slot (imports left out):

a component with one slot (imports left out)
export default defineTaggorComponent({
name: "fallback-card",
tag: "fallback-card",
title: "Fallback card",
description: "A call to action whose title the page can replace; plain HTML stands in for it.",
props: { headline: prop.string({ maxLength: 60, default: "Simule seu financiamento" }) },
slots: { title: { description: "Replaces the default headline." } },
styles: [
":host { display: block; min-height: 120px; padding: 8px; box-sizing: border-box }",
"h3 { margin: 0 0 8px }",
],
render: ({ headline }) => {
const title = useSlot("title");
return h(
"section",
// Exposed for the browser test: what `useSlot` reported on the last render.
{ "data-title-slot": title.hasContent ? "filled" : "empty" },
h("h3", null, title.hasContent ? h("slot", { name: "title" }) : headline),
h("button", { type: "button" }, "Simular"),
);
},
});

The page above is the fixture packages/component/fixtures/fallback-card.html, run in a browser by packages/runtime/e2e/slots.e2e.ts for each row of the table, including a blocked script and a component that throws. The browsers the matrix covers, and what an old browser gets, are in the browser policy of the repository (docs/testing/compatibility.md): the loader does nothing and the page stays as written.

To hold a region of the page until its components are defined, with a time limit that shows this fallback if Taggor does not arrive, see Load Taggor early.