# Load Taggor early

> For a page where most of the content is components - preload hints, a hold on the region with a time limit that shows the fallback, and why the Taggor script never blocks the page.

> Esta página ainda não tem tradução para o português; este é o texto em inglês. (This page is not translated yet: the English text follows.)

Most pages use Taggor for a few components. Some pages are almost all components: a landing page, a
pricing page, a form page. There you want the components to show early and the page not to jump when
they arrive. This guide has the recipe, what it costs and what we measured.

## Why the snippet does not block

The snippet is a script tag with the `async` attribute. The browser downloads it in parallel and never waits for it to parse the
page, so if Taggor is slow, blocked by an ad blocker or down, your page still loads and your fallback
shows ([Fallback content and slots](https://taggor.dev/docs/fallback.md)). Taggor does not offer a render-blocking script:
it would turn a Taggor outage into a blank page on your site. The recipe below gets the same
effect (nothing half-drawn) with a time limit, so the worst case is your own fallback.

## The recipe

### 1. Put the snippet in `<head>`, with the preload hints

```sh
node taggor.mjs sites snippet --preload
```

prints the hints and the snippet. In the portal, open the site, the Install tab and tick **Load Taggor
early**. Paste everything in `<head>` of every page:

<!-- prettier-ignore -->
```html
<link rel="preconnect" href="https://cdn.taggorcdn.com" crossorigin>
<link rel="preload" as="script" href="https://cdn.taggorcdn.com/s/<site-key>.js">
<link rel="modulepreload" href="https://cdn.taggorcdn.com/rt/<runtime-version>/runtime.js">
<script async src="https://cdn.taggorcdn.com/s/<site-key>.js"></script>
```

- The loader tells the browser which runtime version your site is served; the command reads it from
  your site's own loader, so the hint names the version in force today. After a runtime release, run
  the command again and paste the new hint; a hint for an old version only wastes a download, it breaks
  nothing. If the version cannot be read, the command leaves that line out.
- The component bundles are addressed by their content and change with every publish, so they cannot be
  in a fixed snippet. The loader fetches them as soon as it runs.
- Under a Content-Security-Policy, the hints follow the same `script-src` as the snippet: list the CDN
  origin as in [Content-Security-Policy](https://taggor.dev/docs/snippet.md#content-security-policy). We run the three hints
  under a policy that lists only that origin for scripts, and the browser reports no violation.

### 2. Hold the region of the page that depends on Taggor, with a time limit

`node taggor.mjs sites snippet --hold 1` (or the **Hold the page** box in the portal) adds this CSS to
the snippet for you, for 1 to 3 seconds and never more; it is off unless you ask. Or write it yourself:

Give the part of the page that is made of components a class, and hold it until its components are
defined, for at most one second:

```html
<style>
  @media (scripting: enabled) {
    .taggor-region:has(:not(:defined)) {
      visibility: hidden;
      animation: taggor-reveal 0s linear 1s forwards;
    }
  }
  @keyframes taggor-reveal {
    to {
      visibility: visible;
    }
  }
</style>

<main class="taggor-region">
  <finance-simulator rate="1.4">
    <a href="https://example.com/simulator">Simulate on our site</a>
  </finance-simulator>
</main>
```

- `:not(:defined)` matches a component tag until its component is registered, so the region stays
  invisible while the components load and shows the moment the last one is defined.
- The animation lasts 0 seconds and starts after the limit (here `1s`): when it fires, the region shows
  whatever it holds, which is your fallback. Taggor down, blocked or slow costs the visitor at most
  that second, and the page is never blank for longer.
- `visibility: hidden` keeps the region's place, so nothing moves when it shows. Give each component
  tag its final size with `:not(:defined)` as in [Fallback content and slots](https://taggor.dev/docs/fallback.md).
- Hold only the regions made of components, never the whole `<body>`. Text that matters for search
  and reading stays outside the region, or inside the fallback.
- Keep the limit short. One second is a good start; raise it only when you have seen your components
  take longer on a real connection, and never above three seconds.
- In a browser without JavaScript the rule does not apply (`scripting: enabled`), and in a browser that does
  not know `:has` the whole rule is ignored: both show the fallback at once.

**Do not use the hold on a page that disables animations.** The hold ends by a CSS animation, so a rule such as
`* { animation: none !important }` (a reduced-motion reset, a "disable animations" tool or plugin) cancels it, and with Taggor blocked or
down the region then stays invisible. Check your page's CSS first; if it has such a rule, skip the hold and keep the fallback visible.

Do not hide the fallback without the time limit: if the script never arrives, the content stays hidden
for good.

### 3. Load a component later, if it is low on the page

A component far down the page does not need its bundle at the first paint. Tell Taggor to fetch it once the page is idle:

```sh
node taggor.mjs attach <component> --loading idle
```

(or **When the page loads this component** in the portal, on the site's component props). `eager`, the default, imports on load.
`idle` waits for a quiet moment of the page, at most two seconds, and then imports the bundle; the runtime itself still loads at once, so
the saving is the component's own bytes. Keep its fallback in the HTML and reserve its size with `:not(:defined)` so nothing jumps when
it shows. If the wait cannot run, the component simply loads on load. A third value, `visible`, is not available yet.

## What we measured

A test page with two components, the snippet in `<head>`, Chromium with a throttled network, one cold visit
per run and the median of five runs. The time is from the start of the navigation to the first rendered
component (the harness serves the bytes uncompressed, about three times what the CDN sends):

| Network                      | No hints | Only preconnect and loader preload | All three hints |
| ---------------------------- | -------- | ---------------------------------- | --------------- |
| Slow 4G (150 ms, 1.6 Mbit/s) | 999 ms   | 998 ms                             | 915 ms (-84 ms) |
| Fast 4G (100 ms, 9 Mbit/s)   | 496 ms   | 496 ms                             | 482 ms (-14 ms) |

Most of the gain is the `modulepreload` of the runtime: it makes the runtime download in parallel with
the loader instead of after it. The `preconnect` and the loader `preload` add nothing when the snippet is
already in `<head>` (the browser finds the script tag as early as the hint), but they help a page where the
snippet sits late in the body. The gain grows as the connection gets slower, which is where an early
component matters most.

## Check it

- Block the Taggor script in your browser's developer tools (Network, block request URL) and reload: the
  region must show your fallback after the limit, with a working link or text.
- Throttle the network to Slow 4G and reload: the components should show without the page moving.
- In the Network panel, the runtime request should start before the loader request finishes. If it does not,
  or it is a different version from the one the loader fetches, run `sites snippet --preload` again.
