---
date: 2026-08-06
title: What's new in @studiometa/ui 1.10.0
description: 'A tour of @studiometa/ui 1.10.0, the release that makes the library usable with zero build step: declarative autoloading through the new @studiometa/ui/autoload entry, one <script> from an ESM CDN, custom manifests for your own components, a dedicated @studiometa/ui-mapbox package, and a new Toaster.'
tags: studiometa-ui, autoloading, progressive-enhancement, javascript, release
---

# What's new in @studiometa/ui 1.10.0

06/08/2026 in #studiometa-ui #release

[`@studiometa/ui`](https://ui.studiometa.dev) 1.10.0 is less about new components and more about **how you load the library**. The headline is autoloading: drop one `<script type="module">` on a page and every component mounts itself from its `data-component` attribute, with no bundler and no manual registration. It ships a new [`@studiometa/ui/autoload`](https://ui.studiometa.dev/guide/autoloading/) entry powered by [`@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev), adds a new [`@studiometa/ui-mapbox`](https://ui.studiometa.dev/reference/items/MapboxMap/) package for building Mapbox maps, introduces a [`Toaster`](https://ui.studiometa.dev/reference/items/Toaster/) notifications component, and modernises the build under the hood.

## Zero-build autoloading

The core of the release is the [autoload runtime in `@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev): a small, generic runtime that scans the page for `data-component` tokens, imports each matching component on demand, and registers it. `@studiometa/ui` exposes it through a single `@studiometa/ui/autoload` side-effect entry — importing it is the whole contract; there is nothing to call.

Load it from an ESM CDN such as [esm.sh](https://esm.sh), which serves the package as native ES modules and resolves its peer dependencies for you:

```html
<script type="module">
  import 'https://esm.sh/@studiometa/ui@next/autoload';
</script>

<!-- Mounts on its own, no build step -->
<button data-component="Action" data-on:click="alert('Hello, no build step!')">Click me</button>
```

The exact same entry works when you bundle from npm: your bundler resolves it from `node_modules` and the behaviour is identical:

```js
import '@studiometa/ui/autoload'; // @studiometa/ui components
import '@studiometa/ui-mapbox/autoload'; // @studiometa/ui-mapbox components
```

Import both entries together and they coalesce into a **single** loader over the composed set, so only one runtime ever scans the DOM.

### Loading strategies

Each component carries a default strategy, overridable per element with `data-load`. A component loads `eager` (immediately), `visible` (near the viewport), `idle` (when the browser is idle), or on `interaction` (first hover, touch or focus):

```html
<div data-component="ScrollAnimation" data-load="visible">…</div>
```

For above-the-fold components that must be ready right away, list them in a `<meta>` element and they load eagerly regardless of their default:

```html
<meta name="js-toolkit:eager" content="Accordion, Action, Modal" />
```

See the [Autoloading guide](https://ui.studiometa.dev/guide/autoloading/) for the full picture: discovery, diagnostics and limitations.

## Bring your own components

Because the autoloader is generic and works purely from a manifest, you can autoload **your own** js-toolkit components alongside the packaged ones. `defineManifest` describes a package: its name, a default loading strategy, and a `modules` record that maps each `data-component` token to a dynamic import. `registerManifests` registers one or more manifests with the shared runtime, and the last one wins on token collisions. Both come from [`@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev):

```js
import { registerManifests, defineManifest } from '@studiometa/js-toolkit';
import { manifest as uiManifest } from '@studiometa/ui/manifest';

const manifest = defineManifest({
  packageName: '@my/app',
  strategy: 'visible',
  modules: {
    MyComponent: () => import('./MyComponent.ts'),
  },
});

registerManifests(uiManifest, manifest);
```

Each entry points at a standard js-toolkit component, and the key you use in `modules` is the token the autoloader matches against `data-component`:

```js
// MyComponent.ts
import { Base } from '@studiometa/js-toolkit';

export default class MyComponent extends Base {
  static config = {
    name: 'MyComponent',
  };
}
```

Writing the `modules` record by hand is fine for a few components. To register a whole folder at once, two adapters (`fromMetaGlob` for Vite, `fromWebpackContext` for webpack) turn a bundler glob into the same record:

```js
const app = defineManifest({
  packageName: '@my/app',
  strategy: 'visible',
  modules: fromMetaGlob(import.meta.glob('./components/*/*.ts')),
});
```

With the glob adapters, the `data-component` token is derived from each file's name, so a `MyComponent.ts` mounts wherever `data-component="MyComponent"` appears, right next to the library's own components. The full workflow, including per-token overrides and constraints, is in the [js-toolkit autoload API reference](https://js-toolkit.studiometa.dev/api/autoload).

## A new package for Mapbox

1.10.0 introduces [`@studiometa/ui-mapbox`](https://ui.studiometa.dev/reference/items/MapboxMap/), a new package of js-toolkit components for building Mapbox maps: `MapboxMap`, markers, popups, clustering and a [`StoreLocator`](https://ui.studiometa.dev/reference/items/StoreLocator/).

```twig
<!-- mapbox-demo.twig -->
<link rel="stylesheet" href="https://esm.sh/mapbox-gl@3.13.0/dist/mapbox-gl.css" />

<div
  data-component="MapboxMap"
  data-option-access-token="pk.eyJ1IjoiYWdlbmNlc3R1ZGlvbWV0YSIsImEiOiJjbXM5NTBycHMwa2Z4MndxeW02YndscmQyIn0.UitMikKpq-TRJOXM-rBcMg"
  data-option-zoom="10"
  data-option-center="[2.35, 48.86]"
  data-option-map-options='{"style":"mapbox://styles/mapbox/streets-v12"}'
  class="h-96 w-full">
  <div data-ref="container" class="h-full w-full"></div>
</div>
```

```ts
// mapbox-demo.ts
import { registerComponent } from '@studiometa/js-toolkit';
import { MapboxMap } from '@studiometa/ui-mapbox';

registerComponent(MapboxMap);
```

The `mapbox-gl` dependency is resolved lazily and not bundled by default. **You provide it**, which keeps you in control of its version and its Web Worker. In a no-build setup you point an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) at the source of your choice.

```html
<script type="importmap">
  { "imports": { "mapbox-gl": "https://esm.sh/mapbox-gl@3" } }
</script>
<script type="module">
  import 'https://esm.sh/@studiometa/ui-mapbox@next/autoload';
</script>
```

In a bundled app, both `mapbox-gl` and `@mapbox/mapbox-gl-geocoder` are resolved from the installed NPM dependencies by default. You can also inject them through the [`provideMapboxGl` / `provideMapboxGeocoder` helpers](https://ui.studiometa.dev/reference/items/MapboxMap/js-api.html#providing-the-mapbox-gl-dependency) if you need more control.

## New component: Toaster

[`Toaster`](https://ui.studiometa.dev/reference/items/Toaster/) is a headless notifications region. It is built on two permanent `aria-live` regions: a polite one for info and success, an assertive one for errors, so toasts are announced to screen readers without ever moving focus. Each toast is a `Timer`-based `Toast` with a pausable auto-dismiss countdown, and the stack animates through the `viewTransition` scheduler.

```twig
<!-- demo-toaster.twig -->
<div class="flex flex-col items-start gap-4">
  <div class="flex flex-wrap gap-3">
    <button
      type="button"
      data-component="Action"
      data-on:click="Toaster->target.show('Your changes have been saved.', { type: 'success' })"
      class="px-4 py-2 rounded bg-emerald-600 text-white font-semibold">
      Save (success)
    </button>
    <button
      type="button"
      data-component="Action"
      data-on:click="Toaster->target.show('Something went wrong.', { type: 'error' })"
      class="px-4 py-2 rounded bg-red-600 text-white font-semibold">
      Fail (error)
    </button>
  </div>

  <p class="max-w-md text-current/60">
    Each click clones the template into the matching live region and starts a pausable auto-dismiss
    countdown. Hover a toast to pause it, or dismiss it with the close button.
  </p>

  {# The region markup is fixed for accessibility: two permanent aria-live
     regions, and one <template> the Toaster clones per notification. #}
  <div
    data-component="Toaster"
    class="pointer-events-none fixed inset-0 z-50 flex flex-col items-end justify-end gap-2 p-4">
    <div
      data-ref="polite"
      aria-live="polite"
      aria-atomic="false"
      aria-relevant="additions"
      class="flex w-full flex-col items-end gap-2"></div>
    <div
      data-ref="assertive"
      role="alert"
      aria-live="assertive"
      aria-atomic="false"
      aria-relevant="additions"
      class="flex w-full flex-col items-end gap-2"></div>

    <template data-ref="template">
      <div
        class="toast pointer-events-auto flex w-80 items-start gap-3 rounded-lg border-l-4 border-blue-500 bg-white p-4 text-gray-900 shadow-lg">
        <p data-message class="min-w-0 flex-1 text-sm"></p>
        <button
          type="button"
          data-ref="close"
          aria-label="Dismiss notification"
          class="text-gray-400 hover:text-gray-900">
          &times;
        </button>
      </div>
    </template>
  </div>
</div>
```

```ts
// demo.ts
import { registerComponents } from '@studiometa/js-toolkit';
import { Action, Toaster, Toast } from '@studiometa/ui';

// The demo shares this one registration. `Toaster` clones its template into a
// `Toast` per notification, so both are registered here.
registerComponents(Action, Toaster, Toast);
```

You provide the markup (one `<template>` the `Toaster` clones per notification) and trigger toasts declaratively from anywhere with [`Action`](https://ui.studiometa.dev/reference/items/Action/):

```html
<button
  type="button"
  data-component="Action"
  data-on:click="Toaster->target.show('Your changes have been saved.', { type: 'success' })">
  Save
</button>
```

## Powered by @studiometa/js-toolkit 3.9.0

The autoloading described above is not a `@studiometa/ui` feature — it is a new **framework-level** capability in [`@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev) 3.9.0. Any js-toolkit application can autoload its own components the same way; `@studiometa/ui` 1.10.0 simply depends on it and ships the thin `@studiometa/ui/autoload` entry. The `defineManifest` / `registerManifests` helpers and the `fromMetaGlob` / `fromWebpackContext` adapters are exported from `@studiometa/js-toolkit`, and documented in its own [autoloading guide](https://js-toolkit.studiometa.dev/guide/going-further/autoloading) and [API reference](https://js-toolkit.studiometa.dev/api/autoload).

The same release also gives js-toolkit **per-symbol subpath imports**: every export is now resolvable at its own extensionless subpath — `@studiometa/js-toolkit/Base`, `@studiometa/js-toolkit/utils/damp`, `@studiometa/js-toolkit/registerManifests` — each importable as a named _or_ default export. Unbundled and CDN consumers can pull in just the one helper they need instead of the whole barrel.

## Under the hood

We migrated every `export *` barrel re-export to explicit named `export { … }` matching the same change in [`@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev) 3.8.1. This helps improve static analysis of the packages and bundler performance when importing them.

## Links

- [`@studiometa/ui` documentation](https://ui.studiometa.dev) and its [changelog](https://github.com/studiometa/ui/blob/main/CHANGELOG.md)
- [Autoloading guide](https://ui.studiometa.dev/guide/autoloading/) and the [js-toolkit autoload runtime](https://js-toolkit.studiometa.dev)
- Custom components: `defineManifest`, `registerManifests` and the glob adapters from [`@studiometa/js-toolkit`](https://js-toolkit.studiometa.dev/api/autoload)
- New component: [`Toaster`](https://ui.studiometa.dev/reference/items/Toaster/)
- Maps: [`@studiometa/ui-mapbox`](https://ui.studiometa.dev/reference/items/MapboxMap/)
