@smoothbundle/loader
v2.2.2
Published
Smooth Bundle framework-aware asset loader
Readme
@smoothbundle/loader
Framework-aware loader for Smooth Bundle assets.
@smoothbundle/loader reads the same .sbundle.json project configuration used by the Smooth Bundle CLI and gives your app typed helpers and ready-to-use components for loading images, scripts, styles and fetchable assets from Smooth Bundle.
It includes:
- a framework-neutral core loader,
- React and Next.js components/hooks for images, audio, video, scripts and styles,
- Vue and Nuxt helpers,
- Astro, SvelteKit and Remix entrypoints,
- TypeScript asset typing generated from
.sbundle.json.
Image, media, script and style components share the same loading semantics as Smooth Bundle HTML snippets. Use isCritical for assets needed above the fold; the loader then applies the correct loading attributes. Image responsiveness is driven only by imageVariants in .sbundle.json.
Installation
npm install @smoothbundle/loaderConfiguration
Place .sbundle.json in the root of your project, generate the loader module, then call its bootstrap once from your app entrypoint.
sbundle typesimport { configureSmoothBundle } from "./smoothbundle-loader";
configureSmoothBundle();After that, import components and helpers directly from @smoothbundle/loader. The generated file adds project asset types globally, so assetPath suggestions work without importing a project-specific wrapper in every component.
The loader expects the standard Smooth Bundle config shape:
{
"userSlug": "your-team",
"projectSlug": "frontend-app",
"version": "1.0.0",
"sources": ["./public/*", "./dist/**/*"],
"excludes": ["./public/*.txt", "./dist/**/*.map"],
"replacePath": {
"/dist/": "/assets/"
},
"imageVariants": "basic"
}Use replacePath when files are collected from one location but should be referenced from another public path. replacePaths is also accepted as an alias.
URLs use the standard Smooth Bundle path:
buildUrl("/public/logo.svg");
// https://cdn.smoothbundle.com/<userSlug>/<projectSlug>/<version>/public/logo.svg
// When version is not configured:
// https://cdn.smoothbundle.com/<userSlug>/<projectSlug>/public/logo.svgTypeScript Asset Typing
The package exposes a generator that reads root .sbundle.json, scans configured sources, applies excludes and replacePath, and writes a TypeScript module with project asset types.
Generate the types with the Smooth Bundle CLI:
sbundle typesThe command should generate:
smoothbundle-loader.tsTo write the file somewhere else:
sbundle types --output src/smoothbundle-loader.tsOnce the generated module is included in your app bootstrap, components imported from @smoothbundle/loader use those generated asset types automatically:
import { SmoothImage } from "@smoothbundle/loader/react";
export function Logo() {
return <SmoothImage assetPath="/public/logo.svg" alt="Smooth Bundle" width={40} height={40} />;
}Commit smoothbundle-loader.ts with your app if you want asset-path autocomplete to work without regenerating it on every install. The .sbundle directory can stay ignored.
.sbundle/*
!.sbundle/Core
Use the core loader when you need a delivery URL or an imperative load outside a component.
import { createLoader } from "@smoothbundle/loader";
const loader = createLoader();
// Build a delivery URL for href, src, metadata or third-party APIs.
const logoUrl = loader.url("/public/logo.svg");
document.querySelector("a.download-logo")?.setAttribute("href", logoUrl);
// Load browser assets imperatively.
await loader.script("/public/app.js");
await loader.style("/public/app.css");
await loader.image("/public/hero.png");
// Fetch a delivered JSON or XML asset.
const response = await loader.fetch("/public/data.json");
const data = await response.json();React
import { SmoothImage, useFetch, useImage } from "@smoothbundle/loader/react";
export function Logo() {
return (
<SmoothImage
assetPath="/public/logo.svg"
alt="Smooth Bundle"
width={40}
height={40}
/>
);
}
export function Preview() {
const { image, error } = useImage("/public/logo.svg");
if (error) return null;
return <span>{image ? "Loaded" : "Loading"}</span>;
}
export function DataBlock() {
const { data } = useFetch("/public/data.json");
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Next.js
import { SmoothAudio, SmoothImage, SmoothVideo, SmoothScript, SmoothStyle } from "@smoothbundle/loader/next";
export default function Page() {
return (
<>
<SmoothStyle assetPath="/public/app.css" isCritical />
<SmoothScript assetPath="/public/app.js" isCritical />
<SmoothImage
assetPath="/public/logo.svg"
alt="Smooth Bundle"
isCritical
width={80}
height={80}
/>
<SmoothAudio
assetPath="/public/intro.mp3"
controls
/>
<SmoothVideo
assetPath="/public/demo.mp4"
posterAssetPath="/public/demo-poster.jpg"
isCritical
controls
width={1280}
height={720}
/>
</>
);
}Vue
<script setup lang="ts">
import { SmoothImage, useImage } from "@smoothbundle/loader/vue";
const { image, error } = useImage("/public/logo.svg");
</script>
<template>
<SmoothImage
asset-path="/public/logo.svg"
alt="Smooth Bundle"
width="80"
height="80"
/>
<span v-if="image">Loaded</span>
<span v-if="error">Failed</span>
</template>Nuxt
Create a plugin that configures the loader once.
// plugins/smoothbundle.ts
import { configureSmoothBundle } from "../smoothbundle-loader";
export default defineNuxtPlugin(() => {
configureSmoothBundle();
});Use the Nuxt entrypoint in components or composables:
<script setup lang="ts">
import { createSmoothLoader, SmoothImage } from "@smoothbundle/loader/nuxt";
const { url } = createSmoothLoader();
const logoUrl = url("/public/logo.svg");
</script>
<template>
<SmoothImage
asset-path="/public/logo.svg"
alt="Smooth Bundle"
width="80"
height="80"
/>
<a :href="logoUrl">Open logo</a>
</template>Astro
---
import { createSmoothLoader } from "@smoothbundle/loader/astro";
const { url } = createSmoothLoader();
const logoUrl = url("/public/logo.svg");
---
<img src={logoUrl} alt="Smooth Bundle" width="80" height="80" />SvelteKit
Initialize the loader in a shared module:
// src/lib/smoothbundle.ts
import { createSmoothLoader } from "@smoothbundle/loader/sveltekit";
export const smoothbundle = createSmoothLoader();Use it in a Svelte component:
<script lang="ts">
import { smoothbundle } from "$lib/smoothbundle";
const logoUrl = smoothbundle.url("/public/logo.svg");
</script>
<img src={logoUrl} alt="Smooth Bundle" width="80" height="80" />Or create a typed load helper:
import { createAssetLoad } from "@smoothbundle/loader/sveltekit";
export const load = createAssetLoad({
assetPath: "/public/logo.svg",
depends: ["smoothbundle:logo"],
cacheControl: "public, max-age=3600"
});Remix
import { createSmoothLoader, createStyleLink } from "@smoothbundle/loader/remix";
const { url } = createSmoothLoader();
export const links = () => [
createStyleLink("/public/app.css")
];
export default function Route() {
return (
<img
src={url("/public/logo.svg")}
alt="Smooth Bundle"
width={80}
height={80}
/>
);
}Generated Image Variants
If your .sbundle.json contains imageVariants, generated type output also includes those preset labels as valid variants. Use "basic" for sm, lg, and xl, or "extended" for xs, sm, md, lg, xl, and xxl.
const loader = createLoader();
loader.image("/public/logo.png", "sm");When .sbundle.json contains imageVariants, image components automatically build srcSet from those configured widths. When imageVariants is not configured, the same component renders a single CDN src.
Pass maxVariant to cap the generated srcSet at a configured image variant. The component still respects width, so it only includes variants that are renderable up to the smaller of maxVariant and width.
<SmoothImage
assetPath="/public/card.png"
alt="Card"
width={640}
height={360}
maxVariant="sm"
/>isCritical controls loading behavior. Critical images get fetchpriority="high" and synchronous decoding. Non-critical images get loading="lazy" and async decoding.
import { SmoothImage } from "@smoothbundle/loader/react";
export function HeroImage() {
return (
<SmoothImage
assetPath="/public/hero.png"
alt="Hero"
isCritical
width={1536}
height={1024}
/>
);
}