npm install @meginhufr/brua-webWeb
React, Vue, Svelte and plain HTML: one package, a provider with your base URL, then components by id.
The package has the AssetResponse types, the variant selection rule, <picture> rendering and a component for each framework. Every renderer puts the dark-mode files behind a media query and AVIF and WebP as sources, and sets alt, lang, width and height on the <img>, so the browser picks the file and dark mode switches live. Animations show their still.
Install
npm install @meginhufr/brua-webReact, Vue and Svelte are optional peers: install the one you use.
Point it at your organization
Every released asset of your organization lives under one base URL on the edge, with no key. The examples use Brua's own organization; yours is on the Integrate page of the back office, or comes from the colleague who set Brua up.
import { createBrua } from "@meginhufr/brua-web";
const brua = createBrua({ base: "https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44", lang: "en" });
const asset = await brua.asset("bridge"); // undefined when nothing is released under the idThe client reads {base}/{id}.json, keeps it for a minute, the edge's own lifetime, and shares it with every component that asks for the same id.
Render by id
import { BruaProvider, BruaPicture } from "@meginhufr/brua-web/react";
<BruaProvider base="https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44" lang="en">
<BruaPicture id="bridge" sizes="(min-width: 600px) 50vw, 100vw" fallback={<div className="hero skeleton" />} />
</BruaProvider><script setup>
import { BruaPicture, provideBrua } from "@meginhufr/brua-web/vue";
provideBrua({ base: "https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44", lang: "en" }); // once, in the root component
</script>
<BruaPicture id="bridge" sizes="(min-width: 600px) 50vw, 100vw"><div class="hero skeleton" /></BruaPicture><script>
import { BruaPicture, setBrua } from "@meginhufr/brua-web/svelte";
setBrua({ base: "https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44", lang: "en" }); // once, in the root component
</script>
<BruaPicture id="bridge" sizes="(min-width: 600px) 50vw, 100vw"><div class="hero skeleton"></div></BruaPicture>// Without a framework: the client gives the asset, pictureHtml renders it.
import { createBrua, pictureHtml } from "@meginhufr/brua-web";
const brua = createBrua({ base: "https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44", lang: "en" });
const asset = await brua.asset("bridge");
if (asset) element.innerHTML = pictureHtml(asset, { sizes: "(min-width: 600px) 50vw, 100vw" });Until the asset has loaded the component renders its fallback: the fallback prop, the default slot or the children. Attributes such as class go on the <img>. The component also takes asset instead of id, for a snapshot your backend embedded in its own JSON.
Where a <picture> can't go, such as a CSS background or a canvas, useBruaUrl(asset, { targetWidth: 320 }) in React, useBruaUrl in Vue and bruaUrl in Svelte give one URL that follows the appearance, Reduce Motion and the display.
What you get
- Light and dark from the system, switching live, with no request of yours.
- The still of an animation under Reduce Motion, and the still as the loading placeholder otherwise. Lottie and Rive are rendered by their own players from the variant URLs.
- The density and width the display needs, from the asset's scales or width ladder, and AVIF, WebP or PNG as the browser supports.
- The alt text in the language you asked for, on the
<img>, withlangset; an empty alt for decorative assets. - A merge in Brua reaches the page within a minute, with no deploy of yours.
Next
- Keep the ids typed: the command line writes the released ids into your code as constants.
- The lifecycle of an asset: what a merge changes, and when.
- The AssetResponse: the JSON the client reads.