@smoothcdn/loader
v1.0.4
Published
Smooth CDN framework-aware asset loader
Readme
@smoothcdn/loader
Framework-aware loader for Smooth CDN assets.
@smoothcdn/loader reads the same .scdn.json project configuration used by the Smooth CDN CLI and gives your app typed helpers and ready-to-use components for loading images, scripts, styles and fetchable assets from Smooth CDN.
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
.scdn.json.
Image, media, script and style components share the same loading semantics as Smooth CDN 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 .scdn.json.
Installation
npm install @smoothcdn/loaderConfiguration
Place .scdn.json in the root of your project, generate the loader module, then call its bootstrap once from your app entrypoint.
scdn typesimport { configureSmoothCDN } from "./smoothcdn-loader";
configureSmoothCDN();After that, import components and helpers directly from @smoothcdn/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 CDN config shape:
{
"userSlug": "your-team",
"projectSlug": "frontend-app",
"version": "1.0.0",
"sources": ["./public/*", "./dist/**/*"],
"excludes": ["./public/*.txt", "./dist/**/*.map"],
"replacePath": {
"/dist/": "/assets/"
},
"imageVariants": {
"mobile": 360,
"tablet": 720,
"desktop": 1600
}
}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 CDN path:
buildUrl("/public/logo.svg");
// https://cdn.smoothcdn.com/<userSlug>/<projectSlug>/<version>/public/logo.svg
// When version is not configured:
// https://cdn.smoothcdn.com/<userSlug>/<projectSlug>/public/logo.svgTypeScript Asset Typing
The package exposes a generator that reads root .scdn.json, scans configured sources, applies excludes and replacePath, and writes a TypeScript module with project asset types.
Generate the types with the Smooth CDN CLI:
scdn typesThe command should generate:
smoothcdn-loader.tsTo write the file somewhere else:
scdn types --output src/smoothcdn-loader.tsOnce the generated module is included in your app bootstrap, components imported from @smoothcdn/loader use those generated asset types automatically:
import { CDNImage } from "@smoothcdn/loader/react";
export function Logo() {
return <CDNImage assetPath="/public/logo.svg" alt="Smooth CDN" width={40} height={40} />;
}Commit smoothcdn-loader.ts with your app if you want asset-path autocomplete to work without regenerating it on every install. The .scdn directory can stay ignored.
.scdn/*
!.scdn/Core
Use the core loader when you need a CDN URL or an imperative load outside a component.
import { createLoader } from "@smoothcdn/loader";
const loader = createLoader();
// Build a CDN 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 CDN-hosted JSON or XML asset.
const response = await loader.fetch("/public/data.json");
const data = await response.json();React
import { CDNImage, useFetch, useImage } from "@smoothcdn/loader/react";
export function Logo() {
return (
<CDNImage
assetPath="/public/logo.svg"
alt="Smooth CDN"
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 { CDNAudio, CDNImage, CDNVideo, Script, Style } from "@smoothcdn/loader/next";
export default function Page() {
return (
<>
<Style assetPath="/public/app.css" isCritical />
<Script assetPath="/public/app.js" isCritical />
<CDNImage
assetPath="/public/logo.svg"
alt="Smooth CDN"
isCritical
width={80}
height={80}
/>
<CDNAudio
assetPath="/public/intro.mp3"
controls
/>
<CDNVideo
assetPath="/public/demo.mp4"
posterAssetPath="/public/demo-poster.jpg"
isCritical
controls
width={1280}
height={720}
/>
</>
);
}Vue
<script setup lang="ts">
import { CDNImage, useImage } from "@smoothcdn/loader/vue";
const { image, error } = useImage("/public/logo.svg");
</script>
<template>
<CDNImage
asset-path="/public/logo.svg"
alt="Smooth CDN"
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/smoothcdn.ts
import { configureSmoothCDN } from "../smoothcdn-loader";
export default defineNuxtPlugin(() => {
configureSmoothCDN();
});Use the Nuxt entrypoint in components or composables:
<script setup lang="ts">
import { createSmoothLoader, CDNImage } from "@smoothcdn/loader/nuxt";
const { url } = createSmoothLoader();
const logoUrl = url("/public/logo.svg");
</script>
<template>
<CDNImage
asset-path="/public/logo.svg"
alt="Smooth CDN"
width="80"
height="80"
/>
<a :href="logoUrl">Open logo</a>
</template>Astro
---
import { createSmoothLoader } from "@smoothcdn/loader/astro";
const { url } = createSmoothLoader();
const logoUrl = url("/public/logo.svg");
---
<img src={logoUrl} alt="Smooth CDN" width="80" height="80" />SvelteKit
Initialize the loader in a shared module:
// src/lib/smoothcdn.ts
import { createSmoothLoader } from "@smoothcdn/loader/sveltekit";
export const smoothcdn = createSmoothLoader();Use it in a Svelte component:
<script lang="ts">
import { smoothcdn } from "$lib/smoothcdn";
const logoUrl = smoothcdn.url("/public/logo.svg");
</script>
<img src={logoUrl} alt="Smooth CDN" width="80" height="80" />Or create a typed load helper:
import { createAssetLoad } from "@smoothcdn/loader/sveltekit";
export const load = createAssetLoad({
assetPath: "/public/logo.svg",
depends: ["smoothcdn:logo"],
cacheControl: "public, max-age=3600"
});Remix
import { createSmoothLoader, createStyleLink } from "@smoothcdn/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 CDN"
width={80}
height={80}
/>
);
}Generated Image Variants
If your .scdn.json contains imageVariants, generated type output also includes those labels as valid variants.
const loader = createLoader();
loader.image("/public/logo.png", "640");When .scdn.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.
isCritical controls loading behavior. Critical images get fetchpriority="high" and synchronous decoding. Non-critical images get loading="lazy" and async decoding.
import { CDNImage } from "@smoothcdn/loader/react";
export function HeroImage() {
return (
<CDNImage
assetPath="/public/hero.png"
alt="Hero"
isCritical
width={1536}
height={1024}
/>
);
}