npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mad-core/react

v0.3.0

Published

React components for serving responsive, format-optimized images, videos, Lottie animations, and Rive animations from a MAD server

Downloads

234

Readme

@mad-core/react

React components for serving responsive, format-optimized images, videos, Lottie, and Rive animations from a MAD (Media Assets Distributor) server.

Features

  • <MadImage> — renders a <picture> element with automatic <source> generation for modern formats (avif, webp)
  • <MadVideo> — renders a <video> element with <source> elements for format negotiation (webm, mp4) and optional poster screenshots
  • <MadLottie> — renders a Lottie animation (.json / .lottie) via canvas, with autoplay/loop control
  • <MadRive> — renders a Rive animation (.riv) via canvas, with artboard/state-machine/fit control
  • <MadGallery> — fetches a published gallery and renders its items with a render-prop
  • useMadImage / useMadVideo / useMadAnimation / useMadGallery — hooks for building memoized asset URLs or fetching a gallery
  • buildMadUrl / buildMadVideoUrl / buildMadAnimationUrl / buildMadGalleryUrl — low-level utilities for constructing MAD URLs
  • Art direction — breakpoint-aware responsive sources via the sources prop
  • Path-based URLs — DAM-compatible URL format (/image/w/800/f/webp/my-slug-a7f3b2, /video/f/webm/w/1280/my-slug-a7f3b2, /animation/my-slug-a7f3b2)
  • Tree-shakeable — ESM-only, sideEffects: false
  • TypeScript-first — full type definitions with JSDoc documentation
  • Zero required runtime dependencies — only react as a required peer; @lottiefiles/dotlottie-web and @rive-app/canvas are optional peers (needed only if you use <MadLottie> / <MadRive> respectively)

Installation

# npm
npm install @mad-core/react

# pnpm
pnpm add @mad-core/react

# yarn
yarn add @mad-core/react

react 18 or 19 is required as a peer dependency.

Quick Start

import { MadImage } from "@mad-core/react";

function Hero() {
  return (
    <MadImage
      slug="hero-banner-a7f3b2"
      baseUrl="https://cdn.example.com"
      widths={[400, 800, 1200, 1600]}
      sizes="(max-width: 768px) 100vw, 50vw"
      alt="Hero banner"
      loading="lazy"
    />
  );
}

URL Format

MAD uses path-based transform parameters (DAM-compatible):

https://cdn.example.com/image/w/800/h/600/f/webp/q/80/c/cover/hero-banner-a7f3b2
https://cdn.example.com/video/w/1280/h/720/f/webm/demo-video-a7f3b2
                               ──────────────────────────────── ─────────────────
                               key/value pairs (any order)       slug (always last)

API Reference

<MadImage>

Renders a responsive <picture> element with optimized format sources.

| Prop | Type | Default | Description | |------|------|---------|-------------| | slug | string | — | Required. MAD asset slug | | baseUrl | string | — | Required. MAD server base URL | | params | MadTransformParams | {} | Base transform params applied to all URLs | | widths | number[] | — | Responsive srcset widths | | sizes | string | — | HTML sizes attribute | | formats | Array<"avif" \| "webp" \| "jpg" \| "png"> | ["avif", "webp"] | Format priority list | | sources | MadSource[] | — | Art direction sources | | ...rest | ImgHTMLAttributes | — | Passed to the fallback <img> (alt, className, loading, etc.) |

Art direction example

<MadImage
  slug="hero-banner-a7f3b2"
  baseUrl="https://cdn.example.com"
  widths={[400, 800]}
  sizes="100vw"
  sources={[
    {
      media: "(min-width: 1024px)",
      params: { w: 1200, p: "center" },
      widths: [1200, 1800, 2400],
      sizes: "80vw",
    },
  ]}
  alt="Responsive hero"
/>

useMadImage(params: MadUrlParams): UseMadImageResult

Hook that returns a memoized image URL and a srcSet builder function. Use this when you need full control over the rendered markup.

import { useMadImage } from "@mad-core/react";

function Avatar({ slug }: { slug: string }) {
  const { url, srcSet } = useMadImage({
    slug,
    baseUrl: "https://cdn.example.com",
    w: 128,
    f: "webp",
  });

  return (
    <img
      src={url}
      srcSet={srcSet([128, 256, 384])}
      sizes="128px"
      alt="User avatar"
    />
  );
}

| Return field | Type | Description | |-------------|------|-------------| | url | string | Fully-qualified MAD image URL | | srcSet | (widths: number[]) => string | Generates a srcset string for the given widths |

buildMadUrl(params: MadUrlParams): string

Low-level utility to build a MAD image URL with path-based transform parameters.

import { buildMadUrl } from "@mad-core/react";

const url = buildMadUrl({
  slug: "hero-banner-a7f3b2",
  baseUrl: "https://cdn.example.com",
  w: 800,
  f: "webp",
  q: 80,
});
// => "https://cdn.example.com/image/w/800/f/webp/q/80/hero-banner-a7f3b2"

<MadVideo>

Renders a <video> element with <source> elements for format negotiation, serving optimized video from a MAD server.

| Prop | Type | Default | Description | |------|------|---------|-------------| | slug | string | — | Required. MAD asset slug | | baseUrl | string | — | Required. MAD server base URL | | params | MadVideoTransformParams | {} | Base transform params applied to all URLs | | formats | Array<"webm" \| "mp4" \| "ogg"> | ["webm", "mp4"] | Format priority list | | poster | number \| false | — | Time in seconds for a poster screenshot (omit or false to disable) | | ...rest | VideoHTMLAttributes | — | Passed to <video> (autoPlay, muted, loop, controls, className, etc.) |

<MadVideo
  slug="demo-video-a7f3b2"
  baseUrl="https://cdn.example.com"
  params={{ w: 1280, h: 720 }}
  poster={2}
  autoPlay muted loop
/>

useMadVideo(params: MadVideoUrlParams): UseMadVideoResult

Hook that returns a memoized video URL and a posterUrl builder function. Use this when you need full control over the rendered markup.

import { useMadVideo } from "@mad-core/react";

function Player({ slug }: { slug: string }) {
  const { url, posterUrl } = useMadVideo({
    slug,
    baseUrl: "https://cdn.example.com",
    w: 1280,
    f: "webm",
  });

  return <video src={url} poster={posterUrl(2)} controls />;
}

| Return field | Type | Description | |-------------|------|-------------| | url | string | Fully-qualified MAD video URL | | posterUrl | (time: number) => string | Generates a poster screenshot URL at the given seek time |

buildMadVideoUrl(params: MadVideoUrlParams): string

Low-level utility to build a MAD video URL with path-based transform parameters.

import { buildMadVideoUrl } from "@mad-core/react";

const url = buildMadVideoUrl({
  slug: "demo-video-a7f3b2",
  baseUrl: "https://cdn.example.com",
  w: 1280,
  f: "webm",
  dur: 30,
});
// => "https://cdn.example.com/video/w/1280/f/webm/dur/30/demo-video-a7f3b2"

<MadLottie>

Renders a Lottie animation (.json Bodymovin or .lottie dotLottie) served by MAD. The animation is drawn onto a <canvas> element by @lottiefiles/dotlottie-web, which is dynamically imported on mount.

Requires the optional peer dependency:

npm install @lottiefiles/dotlottie-web

| Prop | Type | Default | Description | |------|------|---------|-------------| | slug | string | — | Required. MAD asset slug | | baseUrl | string | — | Required. MAD server base URL | | autoplay | boolean | false | Play immediately after load | | loop | boolean | false | Loop playback | | className | string | — | Class applied to the canvas | | style | CSSProperties | — | Inline style for the canvas |

import { MadLottie } from "@mad-core/react";

<MadLottie
  slug="hero-animation-a7f3b2"
  baseUrl="https://cdn.example.com"
  autoplay
  loop
  className="h-64 w-64"
/>

useMadAnimation(params: MadAnimationUrlParams): UseMadAnimationResult

Hook that returns a memoized animation URL. Use this to wire up your own Lottie player.

import { useMadAnimation } from "@mad-core/react";

function MyPlayer({ slug }: { slug: string }) {
  const { url } = useMadAnimation({
    slug,
    baseUrl: "https://cdn.example.com",
  });
  // feed `url` into lottie-web, dotlottie-react, etc.
}

| Return field | Type | Description | |-------------|------|-------------| | url | string | Fully-qualified MAD animation URL |

buildMadAnimationUrl(params: MadAnimationUrlParams): string

Low-level utility to build a MAD animation URL. Works for both Lottie and Rive assets — the URL shape is the same; the asset's type field (from the management API) tells you which player to use.

import { buildMadAnimationUrl } from "@mad-core/react";

const url = buildMadAnimationUrl({
  slug: "hero-animation-a7f3b2",
  baseUrl: "https://cdn.example.com",
});
// => "https://cdn.example.com/animation/hero-animation-a7f3b2"

<MadRive>

Renders a Rive animation (.riv) served by MAD. The animation is drawn onto a <canvas> element by @rive-app/canvas, which is dynamically imported on mount.

Requires the optional peer dependency:

npm install @rive-app/canvas

| Prop | Type | Default | Description | |------|------|---------|-------------| | slug | string | — | Required. MAD asset slug | | baseUrl | string | — | Required. MAD server base URL | | artboard | string | first artboard | Artboard to activate | | stateMachine | string | — | State machine to activate. Omit to play the default timeline | | fit | "cover" \| "contain" \| "fill" \| "fitWidth" \| "fitHeight" \| "none" | "contain" | Layout fit mode | | alignment | "center" \| "topLeft" \| "topCenter" \| … | "center" | Alignment within the fitted bounds | | autoplay | boolean | false | Play immediately after load | | className | string | — | Class applied to the canvas | | style | CSSProperties | — | Inline style for the canvas |

Rive's loop behavior is configured inside the .riv file at export time, so there is no loop prop — the file's authored setting is authoritative.

import { MadRive } from "@mad-core/react";

<MadRive
  slug="hero-animation-a7f3b2"
  baseUrl="https://cdn.example.com"
  stateMachine="main"
  fit="cover"
  autoplay
  className="h-64 w-full"
/>

Types

MadTransformParams

| Field | Type | Description | |-------|------|-------------| | w | number | Target width in pixels | | h | number | Target height in pixels | | f | MadImageFormat | Output format | | q | number | Output quality (1–100) | | c | MadFit | Resize fit mode | | p | MadPosition | Crop position anchor | | r | [x, y, w, h] | Region crop (extract) | | t | string | Transform: "grayscale", "blur", "blur(5)" | | ro | 90 \| 180 \| 270 | Rotate degrees | | sh | true \| number | Sharpen (true = default, number = sigma) | | fl | MadFlip | Flip: "v" (vertical) or "h" (horizontal) | | bg | string | Background hex color (3, 6, or 8 chars, no #) | | wm | "br" \| "bl" \| "tr" \| "tl" \| "center" | Watermark position (requires watermark uploaded via admin) | | wmo | number | Watermark opacity, 0–100 (default 50) | | wms | number | Watermark scale as % of image width, 1–100 (default 20) |

MadUrlParams

Extends MadTransformParams with:

| Field | Type | Description | |-------|------|-------------| | slug | string | MAD asset slug | | baseUrl | string | MAD server base URL |

MadSource

| Field | Type | Description | |-------|------|-------------| | media | string | CSS media query, e.g. "(min-width: 1024px)" | | params | MadTransformParams | Transform overrides for this breakpoint | | widths | number[] | srcset widths (overrides global) | | sizes | string | HTML sizes attribute for this source |

MadPosition

"center" | "top" | "bottom" | "left" | "right"

MadFit

"cover" | "contain" | "fill" | "inside" | "outside"

MadImageFormat

"webp" | "avif" | "jpg" | "png" | "gif" | "auto"

"auto" enables content negotiation: the server inspects the client's Accept header and picks the best format (avif > webp > jpg).

MadFlip

"v" | "h"

MadVideoTransformParams

| Field | Type | Description | |-------|------|-------------| | w | number | Target width in pixels | | h | number | Target height in pixels | | f | MadVideoFormat | Output format | | time | number | Seek position in seconds (alone = JPEG screenshot) | | dur | number | Duration in seconds (trim video) |

MadVideoUrlParams

Extends MadVideoTransformParams with:

| Field | Type | Description | |-------|------|-------------| | slug | string | MAD asset slug | | baseUrl | string | MAD server base URL |

MadVideoFormat

"mp4" | "webm" | "ogg"

<MadGallery>

Fetches a published gallery from /gallery/:slug and renders its items with a render-prop. Uses plain fetch under the hood (no extra runtime deps); you can inject a custom fetcher to integrate with react-query, SWR, or SSR frameworks.

import { MadGallery } from "@mad-core/react";

function GalleryPage({ slug }: { slug: string }) {
  return (
    <MadGallery
      slug={slug}
      baseUrl="https://cdn.example.com"
      className="grid grid-cols-3 gap-4"
      renderLoading={() => <Spinner />}
      renderError={(err) => <p>{err.message}</p>}
      renderEmpty={() => <p>No items yet</p>}
      renderItem={(item) =>
        item.type === "image" ? (
          <img src={item.thumbnailUrl ?? item.url} alt={item.altText ?? ""} />
        ) : item.type === "video" ? (
          <video src={item.url} poster={item.posterUrl} controls />
        ) : null
      }
    />
  );
}

| Prop | Type | Default | Description | |------|------|---------|-------------| | slug | string | — | Required. Gallery slug | | baseUrl | string | — | Required. MAD server base URL | | renderItem | (item, index) => ReactNode | — | Required. Maps each item to JSX | | renderLoading | () => ReactNode | — | Shown while fetching | | renderError | (err) => ReactNode | — | Shown on fetch error | | renderEmpty | (gallery) => ReactNode | — | Shown when items is empty | | fetcher | MadGalleryFetcher | fetch | Custom fetcher — e.g. react-query | | className | string | — | Class applied to the wrapping <div> |

useMadGallery(params: UseMadGalleryParams): UseMadGalleryResult

Lower-level hook that fetches on mount and exposes { data, isLoading, error, refetch }. Unlike the other SDK hooks (which are pure URL builders), this one performs a network request because gallery payloads only exist server-side.

import { useMadGallery } from "@mad-core/react";

function List({ slug }: { slug: string }) {
  const { data, isLoading, error, refetch } = useMadGallery({
    slug,
    baseUrl: "https://cdn.example.com",
  });
  // …render or invoke refetch()…
}

| Return field | Type | Description | |--------------|------|-------------| | data | MadGalleryResponse \| null | Parsed payload (null while loading or on error) | | isLoading | boolean | True while the fetch is in flight | | error | Error \| null | Non-null when the fetch failed | | refetch | () => void | Re-runs the fetch (aborts any in-flight request) |

buildMadGalleryUrl(params: MadGalleryUrlParams): string

Pure URL builder for SSR or query libs that want to own the fetch.

import { buildMadGalleryUrl } from "@mad-core/react";

const url = buildMadGalleryUrl({
  slug: "summer-trip-a7f3b2",
  baseUrl: "https://cdn.example.com",
});
// => "https://cdn.example.com/gallery/summer-trip-a7f3b2"

MadGalleryResponse

| Field | Type | Description | |-------|------|-------------| | slug | string | Gallery slug | | name | string | Display name | | description | string \| null | — | | namespace | string \| null | Namespace or null for global | | coverUrl | string \| null | Thumbnail of the cover image (explicit or first image-type item) | | publishedAt | string \| null | ISO 8601 timestamp; null = draft | | items | MadGalleryItem[] | Ordered items |

MadGalleryItem

| Field | Type | Description | |-------|------|-------------| | type | "image" \| "video" \| "lottie" \| "rive" | Asset type | | slug | string | MAD asset slug | | url | string | Full delivery URL (filter/adjustments baked-in for images) | | thumbnailUrl? | string | w/400/f/auto thumbnail (images only) | | posterUrl? | string | Video poster JPEG at time/0 | | duration? | number \| null | Video duration in seconds (if known) | | altText | string \| null | Alt text for images / a11y | | width | number \| null | Intrinsic width in px | | height | number \| null | Intrinsic height in px | | tags | string[] | Tags attached to the asset |

How It Works

<MadImage> renders a standard HTML <picture> element:

  1. For each art direction source (sources prop), it generates <source> elements for every format in the formats list, each with the appropriate media, type, srcSet, and sizes attributes.
  2. For global responsive widths (widths prop), it generates <source> elements for each format (no media attribute).
  3. A fallback <img> element is rendered last with the base URL (no forced format), letting the browser negotiate the best option.

The browser evaluates sources top-to-bottom and picks the first match, giving you automatic format negotiation (avif > webp > fallback) with art-direction breakpoints.

<MadVideo> renders a standard HTML <video> element:

  1. For each format in the formats list, it generates a <source> element with the corresponding src and type attributes.
  2. When poster is set, it generates a poster URL using the MAD video screenshot endpoint (/video/time/{seconds}/slug), which returns a JPEG frame at the given seek position.
  3. All remaining props (autoPlay, muted, loop, controls, etc.) are passed through to the <video> element.

License

MIT