nteract elements

Media Router

MIME type-based output dispatcher with extensible renderers

Automatically selects the best renderer for Jupyter outputs based on MIME type priority. Supports custom renderers and priority ordering for platform-specific MIME types.

Installation

npx shadcn@latest add https://nteract-elements.vercel.app/r/media-router.json

Install all output component dependencies, then copy from the registry source.

MediaRouter depends on all built-in output components. Installing it will also install AnsiOutput, MarkdownOutput, HtmlOutput, ImageOutput, SvgOutput, and JsonOutput.

Usage

Basic

import { MediaRouter } from "@/registry/outputs/media-router"

export function CellOutput({ output }) {
  return <MediaRouter data={output.data} metadata={output.metadata} />
}

With Metadata

Metadata is keyed by MIME type, same as data. Used for image dimensions, JSON display settings, etc:

<MediaRouter
  data={{
    "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..."
  }}
  metadata={{
    "image/png": { width: 400, height: 300 }
  }}
/>

Custom Renderers

Register custom renderers for MIME types not supported by the built-ins, or override built-ins:

import { MediaRouter, DEFAULT_PRIORITY } from "@/registry/outputs/media-router"
import { PlotlyChart } from "./plotly-chart"
import { VegaLiteChart } from "./vega-lite-chart"

<MediaRouter
  data={output.data}
  metadata={output.metadata}
  priority={[
    "application/vnd.plotly.v1+json",
    "application/vnd.vegalite.v5+json",
    ...DEFAULT_PRIORITY
  ]}
  renderers={{
    "application/vnd.plotly.v1+json": ({ data }) => (
      <PlotlyChart data={data} />
    ),
    "application/vnd.vegalite.v5+json": ({ data }) => (
      <VegaLiteChart spec={data} />
    ),
  }}
/>

Props

PropTypeDefaultDescription
dataRecord<string, unknown>Output data mapping MIME types to content
metadataRecord<string, object>{}Metadata mapping MIME types to their settings
priorityreadonly string[]DEFAULT_PRIORITYMIME type priority order
renderersRecord<string, CustomRenderer>{}Custom renderer functions
unsafebooleanfalseAllow unsafe HTML rendering (requires iframe)
fallbackReactNodeCustom fallback when no MIME type is supported
loadingReactNodeCustom loading component while lazy-loading
classNamestring""Additional CSS classes

Custom Renderer Props

Custom renderer functions receive:

interface RendererProps {
  data: unknown;              // The content for this MIME type
  metadata: Record<string, unknown>;  // Metadata for this MIME type
  mimeType: string;           // The MIME type being rendered
  className?: string;         // CSS classes from parent
}

Metadata

Built-in metadata support:

MIME TypeMetadataUsage
image/*{ width, height }Image dimensions
application/json{ collapsed }JSON tree collapse depth

Custom renderers receive their metadata directly and can use any keys.

Default Priority

The default MIME type priority (higher = preferred):

import { DEFAULT_PRIORITY } from "@/registry/outputs/media-router"

// DEFAULT_PRIORITY = [
//   "application/vnd.jupyter.widget-view+json",
//   "application/vnd.plotly.v1+json",
//   "application/vnd.vegalite.v5+json",
//   ...
//   "text/html",
//   "text/markdown",
//   "image/svg+xml",
//   "image/png",
//   "image/jpeg",
//   "application/json",
//   "text/plain",
// ]

To add custom types, prepend them to the default:

priority={["application/x-custom", ...DEFAULT_PRIORITY]}

Jupyter Integration

import { MediaRouter } from "@/registry/outputs/media-router"

function CellOutput({ output }) {
  // execute_result and display_data outputs
  if (output.output_type === "execute_result" || 
      output.output_type === "display_data") {
    return <MediaRouter data={output.data} metadata={output.metadata} />
  }
  
  // stream outputs (stdout/stderr)
  if (output.output_type === "stream") {
    return <MediaRouter data={{ "text/plain": output.text }} />
  }
  
  return null
}

Unsafe Mode

For HTML and Markdown outputs that contain scripts, use unsafe={true}. This requires rendering inside an iframe:

// Inside a sandboxed iframe only
<MediaRouter data={outputData} unsafe={true} />

Helper Function

Use getSelectedMimeType to inspect which MIME type would be selected:

import { getSelectedMimeType, DEFAULT_PRIORITY } from "@/registry/outputs/media-router"

const mimeType = getSelectedMimeType(
  { "text/plain": "Hello", "text/html": "<b>Hello</b>" },
  DEFAULT_PRIORITY
)
// Returns: "text/html"

On this page