> ## Documentation Index
> Fetch the complete documentation index at: https://sprincul.sinfullycoded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prevent Flash of Uninitialized UI with data-cloaked

> Add data-cloaked to model containers or the body element to hide content until Sprincul finishes initializing, preventing layout flicker.

When a browser loads a server-rendered page, HTML appears on screen before any JavaScript runs. If your models update visible text, swap classes, or fetch data on startup, users may briefly see placeholder content or the wrong state — a flash of uninitialized UI. Sprincul's cloaking feature lets you hide that content until initialization is complete.

## The problem

Consider a model that fetches a user's name from an API in `afterInit()`. Before the fetch resolves, your HTML shows the default value you set in `beforeInit()` — something like `--`. That blank placeholder flickers into view and then snaps to the real value. Cloaking hides the element entirely until Sprincul is ready to show the correct state.

## The solution: `data-cloaked`

Add the `data-cloaked` attribute to any element you want hidden during initialization. Sprincul removes the attribute when the appropriate initialization phase completes. You supply the CSS rule that does the hiding:

```html theme={null}
<style>
  [data-cloaked] { display: none; }
</style>
```

You control the CSS, so you can use `visibility: hidden`, `opacity: 0`, or any other approach that suits your design.

## Two scopes

Cloaking works at two levels, and they behave differently.

### Model-level cloaking

Add `data-cloaked` to a `data-model` container. Sprincul removes the attribute **after that model's `afterInit` hook completes**, including any async work inside it.

```html theme={null}
<style>
  [data-cloaked] { display: none; }
</style>

<div data-model="Profile" data-cloaked>
  <p data-bind-name="showName"></p>
  <p data-bind-email="showEmail"></p>
</div>
```

If `afterInit()` is `async` and awaits a network request, the element stays hidden until the `await` resolves. This prevents users from seeing incomplete data during hydration.

### Page-level cloaking

Add `data-cloaked` to the `<body>` element. Sprincul removes the attribute **after all models' `afterInit` hooks are called** — but does not wait for them to complete.

```html theme={null}
<body data-cloaked>
  <div data-model="Header">…</div>
  <div data-model="Sidebar">…</div>
  <div data-model="Footer">…</div>
</body>
```

Page-level cloaking is appropriate when you want to delay the first paint until all models have started initializing, but you do not need to wait for async work to finish.

<Warning>
  Page-level cloaking does **not** wait for async `afterInit` hooks to complete. If your models fetch data before rendering, use model-level cloaking instead to prevent showing partial results.
</Warning>

## Choosing the right scope

| Situation                                              | Recommended scope     |
| ------------------------------------------------------ | --------------------- |
| Model fetches data from an API before rendering        | Model-level           |
| Model runs synchronous setup only                      | Either                |
| You want to hide the whole page until all models start | Page-level            |
| Different models have different async timings          | Model-level per model |

<Tip>
  You can mix both scopes on the same page. Apply `data-cloaked` to the `<body>` for a fast initial hide, and also to specific heavy models so they stay hidden until their async work finishes.
</Tip>

## Full HTML + CSS example

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <style>
    [data-cloaked] { display: none; }
  </style>
</head>
<body>
  <!-- Model-level: stays hidden until afterInit() resolves -->
  <div data-model="Profile" data-cloaked>
    <p data-bind-name="showName">--</p>
    <p data-bind-email="showEmail">--</p>
  </div>

  <!-- No cloaking needed: this model initializes synchronously -->
  <div data-model="Counter">
    <button onclick="decrement">-</button>
    <input type="number" data-bind-count="showCount" readonly />
    <button onclick="increment">+</button>
  </div>

  <script type="module" src="./main.js"></script>
</body>
</html>
```

```js theme={null}
// Profile.js
import { SprinculModel } from 'sprincul';

export default class Profile extends SprinculModel {
  beforeInit() {
    this.state.name = '--';
    this.state.email = '--';
  }

  /** @param {HTMLElement} el */
  showName(el) {
    el.textContent = this.state.name;
  }

  /** @param {HTMLElement} el */
  showEmail(el) {
    el.textContent = this.state.email;
  }

  async afterInit() {
    // The element stays hidden until this resolves
    const { name, email } = await fetchUserProfile();
    this.state.name = name;
    this.state.email = email;
  }
}
```

## Cloaking in subsequent `init()` calls

If you call `Sprincul.init()` again to hydrate dynamically added model markup, cloaking works exactly the same way for newly added elements. Sprincul treats freshly added `data-cloaked` attributes on new model roots the same as it does on the first pass.

<Note>
  For more on calling `Sprincul.init()` multiple times to handle dynamic content, see the [Dynamic Content](/guides/dynamic-content) guide.
</Note>
