🧩

Component Reference

Estimated reading time: 60 min

78 components ship across two packages. Props below are read out of the type definitions, so they match the installed version exactly.

@getnarro/core

<AnimatePresence>

AnimatePresence enables the animation of components that have been removed from the tree. When adding/removing more than a single child, every child must be given a unique key prop. Any motion components that have an exit property defined will animate out when removed from the tree. jsx import { motion, AnimatePresence } from 'framer-motion' export const Items = ({ items }) => ( <AnimatePresence> {items.map(item => ( <motion.div key={item.id} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} /> ))} </AnimatePresence> ) You can sequence exit animations throughout a tree using variants. If a child contains multiple motion components with exit props, it will only unmount the child once all motion components have finished animating out. Likewise, any components using usePresence all need to call safeToRemove.

import { AnimatePresence } from "@getnarro/core";

<Canvas>

A container for absolutely positioned elements

<Canvas>
  <CanvasElement x={10} y={20} width={200} height={100}>
    <div>Positioned content</div>
  </CanvasElement>
  <CanvasShape shape="circle" x={50} y={50} size={100} color="#3b82f6" />
</Canvas>
PropTypeRequiredDescription
childrenReactNodeyesChildren elements to position
backgroundstringβ€”Background color
classNamestringβ€”Additional CSS classes
heightstringβ€”Height of the canvas (default: 100%)
widthstringβ€”Width of the canvas (default: 100%)

<CanvasElement>

A positioned element within a Canvas

<CanvasElement x={10} y={20} width={200} height={100}>
  <div>Positioned at 10%, 20%</div>
</CanvasElement>
Using pixels:
```tsx
<CanvasElement x="100px" y="50px" width="200px" height="100px">
  <div>Positioned at 100px, 50px</div>
</CanvasElement>

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | yes | Children content |
| `x` | `PositionValue` | yes | X position (% or px) |
| `y` | `PositionValue` | yes | Y position (% or px) |
| `className` | `string` | β€” | Additional CSS classes |
| `height` | `PositionValue` | β€” | Height (% or px, optional) |
| `opacity` | `number` | β€” | Opacity (0-1) |
| `rotate` | `number` | β€” | Rotation in degrees |
| `width` | `PositionValue` | β€” | Width (% or px, optional) |
| `zIndex` | `number` | β€” | Z-index for layering |

### `<CanvasShape>`

A pre-built shape positioned on the Canvas

```tsx
<Canvas>
  <CanvasShape shape="circle" x={50} y={50} size={100} color="#3b82f6" />
  <CanvasShape shape="star" x={200} y={100} size={80} color="#fbbf24" />
  <CanvasShape shape="arrow" x={300} y={50} size={120} color="#10b981" rotate={45} />
</Canvas>
PropTypeRequiredDescription
shapeShapeTypeyesShape type
sizenumberyesSize of the shape
xPositionValueyesX position (% or px)
yPositionValueyesY position (% or px)
classNamestringβ€”Additional CSS classes
colorstringβ€”Fill color
opacitynumberβ€”Opacity (0-1)
rotatenumberβ€”Rotation in degrees
strokestringβ€”Stroke color
strokeWidthnumberβ€”Stroke width
zIndexnumberβ€”Z-index for layering

<ErrorBoundary>

Error Boundary component that catches errors and displays the overlay

<ErrorBoundary onError={(error) => console.error(error)}>
  <Presentation>
    <Slide>...</Slide>
  </Presentation>
</ErrorBoundary>
PropTypeRequiredDescription
childrenReactNodeyesChildren to render
fallbackReactNode | ((error: ErrorDetails, reset: () => void) => ReactNode)β€”Fallback component to render on error (optional, defaults to ErrorOverlay)
onError((error: Error, errorInfo: ErrorInfo) => void)β€”Callback when an error occurs
showOverlaybooleanβ€”Enable development overlay (default: true in dev, false in prod)

<ErrorOverlay>

Error overlay component for development

<ErrorOverlay
  error={{
    message: "Something went wrong",
    stack: "Error: Something went wrong\n  at Component ..."
  }}
  onDismiss={() => setError(null)}
/>
PropTypeRequiredDescription
errorErrorDetailsyesError details to display
onDismiss(() => void)β€”Callback to dismiss/reset the error
showDismissbooleanβ€”Show dismiss button (default: true)

<MarqueeRow>

One scrolling row of a marquee. Usually you want TrustedByMarquee, which arranges several of these.

PropTypeRequiredDescription
itemsMarqueeItem[]yesItems to display in the marquee
classNamestringβ€”Additional CSS class for the container
direction"left" | "right"β€”Scroll direction
gapnumberβ€”Gap between items in pixels (default: 16)
isActivebooleanβ€”Whether the animation is active
itemMinWidthnumberβ€”Item minimum width in pixels (default: 180)
renderItem((item: MarqueeItem, index: number) => ReactNode)β€”Custom item render function
speednumberβ€”Scroll speed (pixels per frame, default: 0.5)

<MasonryBackground>

A full-bleed masonry grid of images that crossfades between them. Pauses while its slide is off screen, so an idle deck does no work.

<MasonryBackground
  images={["/a.jpg", "/b.jpg", "/c.jpg"]}
  gridColumns={12}
  gridRows={6}
  swapMinIntervalMs={3000}
  swapMaxIntervalMs={6000}
/>
PropTypeRequiredDescription
imagesstring[]yesArray of image URLs to display and cycle through
backgroundColorstringβ€”Background color (default: β€œblack”)
classNamestringβ€”Additional CSS class for the container
gridColumnsnumberβ€”Number of grid columns (default: 12)
gridRowsnumberβ€”Number of grid rows (default: 6)
imageBorderColorstringβ€”Border color for images (default: β€œrgba(255, 255, 255, 0.6)β€œ)
imageBorderWidthstringβ€”Border width for images (default: β€œ1px”)
imageGapnumberβ€”Gap between images in pixels (default: 6)
initialFillTimeMsnumberβ€”Time in ms to fill the page with initial images (default: 800)
isActivebooleanβ€”Whether the animation is active
swapMaxIntervalMsnumberβ€”Maximum time in ms between image swaps after initial fill (default: 2000)
swapMinIntervalMsnumberβ€”Minimum time in ms between image swaps after initial fill (default: 1000)
tileLayoutTileLayout[]β€”Grid tile layout configuration
transitionDurationMsnumberβ€”Duration of crossfade transition in ms (default: 800)

<Motion>

A Framer Motion animation wrapper Provides smooth, physics-based animations with 10+ built-in effects.

<Motion effect="slideUp" duration={0.5}>
  <h1>Animated Title</h1>
</Motion>
PropTypeRequiredDescription
childrenReactNodeyesChildren to animate
classNamestringβ€”Additional class names
delaynumberβ€”Animation delay in seconds
durationnumberβ€”Animation duration in seconds
easingMotionEasingβ€”Easing function
effectMotionEffectβ€”Animation effect to apply
inViewbooleanβ€”Whether to trigger animation only when in view
isActivebooleanβ€”Whether animation has played (for controlled animations)
onAnimationComplete(() => void)β€”Callback when animation completes
respectReducedMotionbooleanβ€”Enable reduced motion fallback
styleCSSPropertiesβ€”Additional styles

<MotionContainer>

Container for staggered child animations

<MotionContainer staggerDelay={0.1}>
  <Motion effect="slideUp">Item 1</Motion>
  <Motion effect="slideUp">Item 2</Motion>
  <Motion effect="slideUp">Item 3</Motion>
</MotionContainer>
PropTypeRequiredDescription
childrenReactNodeyesChildren to animate with stagger
classNamestringβ€”Additional class names
isActivebooleanβ€”Whether children should animate
staggerDelaynumberβ€”Stagger delay between children in seconds
styleCSSPropertiesβ€”Additional styles

<MotionList>

MotionList - Staggered list animation

PropTypeRequiredDescription
itemsReactNode[]yesList items
classNamestringβ€”Additional class names for container
effectMotionEffectβ€”Animation effect for each item
isActivebooleanβ€”Whether animations are active
itemClassNamestringβ€”Additional class names for items
listStyle"none" | "disc" | "decimal" | "check"β€”List style type
staggerDelaynumberβ€”Stagger delay between items in seconds

<MotionNumber>

Animated number counter

<MotionNumber value={1000000} prefix="$" separator duration={2} />
PropTypeRequiredDescription
valuenumberyesTarget value
classNamestringβ€”Additional class names
decimalsnumberβ€”Number of decimal places
delaynumberβ€”Delay before starting
durationnumberβ€”Animation duration in seconds
isActivebooleanβ€”Whether animation is active
prefixstringβ€”Prefix (e.g., ”$β€œ)
separatorbooleanβ€”Use thousands separator
styleCSSPropertiesβ€”Additional styles
suffixstringβ€”Suffix (e.g., ”%β€œ)

<MotionPresence>

MotionPresence - Wrapper for AnimatePresence with slide-aware animations

PropTypeRequiredDescription
childrenReactNodeyesChildren to animate
mode"sync" | "wait" | "popLayout"β€”Animation mode
presenceKeystringβ€”Custom key for tracking

<MotionSpotlight>

Apple Keynote-style Magic Move for images Creates smooth zoom and pan animations between slides. Uses Framer Motion’s layout animations for seamless morphing.

// Slide 1 - Overview
<MotionSpotlight
  layoutId="product"
  src="/product.jpg"
  alt="Product"
  x={50} y={50} scale={1}
  width={800} height={400}
/>

// Slide 2 - Zoomed
<MotionSpotlight
  layoutId="product"
  src="/product.jpg"
  alt="Product detail"
  x={25} y={50} scale={2.5}
  width={800} height={400}
/>
PropTypeRequiredDescription
altstringyesAlt text for accessibility
srcstringyesImage source URL
borderRadiusstring | numberβ€”Border radius
classNamestringβ€”Additional class names
durationnumberβ€”Animation duration in seconds
heightstring | numberβ€”Container height
isActivebooleanβ€”Whether currently active
layoutIdstringβ€”Unique ID for cross-slide morphing
onAnimationComplete(() => void)β€”Callback when animation completes
scalenumberβ€”Scale/zoom level
shadowbooleanβ€”Show shadow
widthstring | numberβ€”Container width
xnumberβ€”X position as percentage (0-100)
ynumberβ€”Y position as percentage (0-100)

<MotionStep>

Individual step within MotionSteps container Shows when the current step is >= this step’s number.

<MotionStep step={1} effect="scaleUp">
  <div>This appears on step 1</div>
</MotionStep>
PropTypeRequiredDescription
childrenReactNodeyesChildren to animate
stepnumberyesStep number (0-indexed) - element shows when currentStep >= this value
classNamestringβ€”Additional class name
durationnumberβ€”Animation duration in seconds
easingMotionEasingβ€”Easing function
effectMotionEffectβ€”Animation effect when revealed
styleCSSPropertiesβ€”Additional styles

<MotionSteps>

Container for step-by-step animations triggered by keystrokes Wrap MotionStep children to create a sequence of animations. Press arrow keys (right/down) to advance, (left/up) to go back.

<MotionSteps totalSteps={3}>
  <MotionStep step={0} effect="scaleUp">
    <div>First element (visible immediately)</div>
  </MotionStep>
  <MotionStep step={1} effect="slideRight">
    <div>Second element (press arrow key)</div>
  </MotionStep>
  <MotionStep step={2} effect="elastic">
    <div>Third element (press again)</div>
  </MotionStep>
</MotionSteps>
PropTypeRequiredDescription
childrenReactNodeyesChildren - should be MotionStep components
classNamestringβ€”Additional class name
loopbooleanβ€”Whether to loop back to start after last step
onComplete(() => void)β€”Callback when all steps are complete
startStepnumberβ€”Starting step (0-indexed)
totalStepsnumberβ€”Total number of steps

<MotionText>

Animated text with various effects

<MotionText type="typewriter" duration={0.05}>
  Hello, World!
</MotionText>
PropTypeRequiredDescription
childrenstringyesText content
classNamestringβ€”Additional class names
delaynumberβ€”Delay before starting in seconds
durationnumberβ€”Animation duration per unit in seconds
isActivebooleanβ€”Whether animation is active
styleCSSPropertiesβ€”Additional styles
type"blur" | "typewriter" | "wave" | "wordByWord" | "charByChar"β€”Animation type

<MotionTransform>

Applies cumulative transforms to a single element across steps Use this inside MotionSteps to animate the SAME element through multiple transformation states (fade, zoom, move, rotate, etc.)

<MotionSteps totalSteps={4}>
  <MotionTransform
    transforms={[
      { opacity: 0 },                           // Step 0 (hidden)
      { opacity: 1 },                           // Step 1 (fade in)
      { opacity: 1, scale: 1.5 },              // Step 2 (zoom)
      { opacity: 1, scale: 1.5, x: 150 },      // Step 3 (move right)
    ]}
  >
    <div className="w-64 h-64 bg-blue-500">
      Transform me!
    </div>
  </MotionTransform>
</MotionSteps>
PropTypeRequiredDescription
childrenReactNodeyesChildren to transform
transformsTransformState[]yesArray of transform states for each step
classNamestringβ€”Additional class name
durationnumberβ€”Animation duration in seconds
easingMotionEasingβ€”Easing function
styleCSSPropertiesβ€”Additional styles

<NebulaBackground>

An animated starfield with nebula glow, behind whatever you put in it.

<NebulaBackground isActive>
  <Heading level={1}>Title over the stars</Heading>
</NebulaBackground>
PropTypeRequiredDescription
backgroundGradientstringβ€”Background gradient (CSS value)
childrenReactNodeβ€”Children to render on top of the background
classNamestringβ€”Additional CSS class for the container
isActivebooleanβ€”Whether the animation is active
nebulaColorsNebulaGlow[]β€”Nebula glow effects configuration
shootingStarDurationnumberβ€”Duration of shooting star animation in ms (default: 2000)
shootingStarFrequencynumberβ€”Time between shooting stars in ms (default: 3000)
starColorsStarColor[]β€”Star color distribution
starLayersStarLayer[]β€”Star layer configuration
twinkleSpeed{ min: number; max: number; }β€”Twinkle speed range in ms

<Notes>

Notes component - Speaker notes Notes are not rendered in the main presentation view. They are meant to be displayed in presenter mode or exported separately.

<Slide>
  <h1>My Slide Title</h1>
  <Notes>
    Remember to explain the key points here.
    - Point 1
    - Point 2
  </Notes>
</Slide>
PropTypeRequiredDescription
childrenReactNodeyesSpeaker notes content

<OverviewGrid>

OverviewGrid component - Displays slides in a grid for quick navigation

PropTypeRequiredDescription
actionsNavigationActionsyesNavigation actions
slidesDataSlideData[]yesSlides data for rendering previews
stateNavigationStateyesNavigation state

<Presentation>

Presentation component - Root container

<Presentation>
  <Slide id="intro">
    <h1>Welcome</h1>
  </Slide>
  <Slide id="content">
    <h2>Main Content</h2>
  </Slide>
</Presentation>
PropTypeRequiredDescription
childrenReactNodeyesChildren slides
aspectRatioAspectRatioβ€”Aspect ratio (default: β€œ16:9”)
autoPlaynumber | boolean | number[]β€”Enable auto-play mode to automatically advance slides. Can be a boolean (uses default 5000ms interval) or a number specifying the interval in milliseconds. Can also be an array of numbers specifying the interval for each slide.
autoPlayLoopbooleanβ€”Whether to loop back to the first slide when auto-play reaches the end. Default: false
baseHeightnumberβ€”Base height for viewport scaling (default: 1080). Content scales proportionally from this baseline
baseWidthnumberβ€”Base width for viewport scaling (default: 1920). Content scales proportionally from this baseline
classNamestringβ€”Additional CSS classes
embeddedbooleanβ€”Enable embedded mode for presentations in smaller containers. When true, viewport scaling uses container size instead of window size. Useful for embedding presentations in modals, cards, or side panels.
enableViewportScalingbooleanβ€”Enable viewport scaling (default: true). Scales content based on viewport size for responsive presentations
faviconstring | nullβ€”Favicon URL or data URI for the presentation. Can be a path to an image file, a data URI, or undefined to use the default Narro favicon. Set to null to disable favicon management (keep existing favicon).
initialSlidenumberβ€”Initial slide index
keepAwakebooleanβ€”Keep the screen awake while the presentation is open (Screen Wake Lock API, best-effort). Useful so the projector/laptop never sleeps mid-talk. Default: false
keyboardbooleanβ€”Enable keyboard navigation (default: true)
maxDurationnumberβ€”Optional maximum duration in minutes for countdown timer in presenter panel
mousebooleanβ€”Enable mouse navigation (default: true)
onAutoPlayComplete(() => void)β€”Callback when auto-play reaches the last slide and completes. Only called when autoPlay is enabled.
onSlideChange((index: number) => void)β€”Callback when slide changes
onSlidesData((slides: SlideData[]) => void)β€”Callback invoked with the registered slide data whenever the slide set changes. Useful for building companion views (notes, overviews) that need access to slide content and speaker notes.
preserveAspectRatiobooleanβ€”Preserve aspect ratio (default: true). When false, slides fill viewport
routingbooleanβ€”Enable URL-based routing (default: true)
showAutoPlayIndicatorbooleanβ€”Show an auto-play indicator icon at the bottom right of the presentation. Shows a play/pause icon during playback and a replay icon when complete. Clicking the replay icon restarts the presentation. Default: false
syncPresentationSyncβ€”Broadcast the current slide to a presenter-sync relay so companion views (e.g. a speaker-notes window on a different port) can follow along live. Pass true to use the default relay URL, or an object to customise it. Default: false
themePresentationThemeβ€”Theme configuration for the presentation
touchbooleanβ€”Enable touch navigation (default: true)
transformTransformConfigβ€”Transform-based animation configuration

<PresenterNotes>

PresenterNotes component - live speaker-notes companion view.

PropTypeRequiredDescription
childrenReactNodeyesThe same slide elements rendered by the deck. Their inline <Notes> are harvested and shown in slide order.
classNamestringβ€”Additional CSS classes for the root container.
currentSlidenumberβ€”Controlled current slide index. When provided, sync is ignored and the view follows this value (useful for tests or custom wiring).
keepAwakebooleanβ€”Keep the screen awake while the notes view is open (best-effort). Default: true (this is a presenter tool).
pacingPresenterPacingβ€”Optional pacing schedule. When provided, the notes view shows an elapsed timer plus an on-time / ahead / over indicator and the planned total.
script((slide: { id: string; index: number; step: number; }) => string | undefined)β€”Optional resolver for a fuller, per-slide script (e.g. a verbatim manuscript) shown in a secondary, scrollable pane next to the notes. Return undefined for slides that have no script. When omitted, no script pane is shown.
syncPresentationSyncβ€”Subscribe to a live deck via the presenter-sync relay. Pass true for the default relay URL or an object to customise it.

<Slide>

Slide component - Individual slide wrapper

<Slide id="intro" layout="title">
  <h1>Welcome to My Presentation</h1>
  <p>Subtitle goes here</p>
</Slide>
With sub-slides
```tsx
<Slide id="multi-step" subSlides={[
{ id: "step1", content: <div>Step 1</div> },
{ id: "step2", content: <div>Step 2</div>, transition: "slide-left" }
]} />

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `__slideIndex` | `number` | β€” | Internal: Explicit slide index from Presentation component |
| `background` | `string` | β€” | Background color or image |
| `children` | `ReactNode` | β€” | Children content |
| `className` | `string` | β€” | Additional CSS classes |
| `id` | `string` | β€” | Unique identifier for the slide |
| `layout` | `LayoutType` | β€” | Layout type (default: "default") |
| `subSlides` | `SubSlide[]` | β€” | Sub-slides for multi-step content within this slide |
| `transition` | `TransitionEffect` | β€” | Transition effect |

### `<SlideContent>`

SlideContent component - Content layout container

```tsx
<Slide>
  <SlideContent layout="centered">
    <h1>Centered Content</h1>
  </SlideContent>
</Slide>
PropTypeRequiredDescription
childrenReactNodeyesChildren content
classNamestringβ€”Additional CSS classes
layout"default" | "centered" | "top" | "bottom"β€”Layout mode

<TransformSlide>

A slide with multiple sub-slides and transform-based navigation

PropTypeRequiredDescription
subSlidesTransformSubSlide[]yesArray of sub-slides with their positions
backgroundstringβ€”Background color or gradient
classNamestringβ€”Additional CSS classes
durationnumberβ€”Transition duration in seconds (default: 1)
easing"linear" | "easeIn" | "easeOut" | "easeInOut" | "circInOut" | "backInOut"β€”Transition easing (default: β€œeaseInOut”)
initialSubSlidenumberβ€”Initial sub-slide index (default: 0)
perspectivebooleanβ€”Enable 3D perspective (default: true)
perspectiveValuenumberβ€”Perspective value in pixels (default: 1000)

<TrustedByMarquee>

Rows of logos or names scrolling infinitely in alternating directions.

<TrustedByMarquee
  rows={[[{ name: "Acme", color: "#ffffff" }], [{ name: "Globex", color: "#60a5fa" }]]}
  isActive
  baseSpeed={40}
/>
PropTypeRequiredDescription
rowsMarqueeItem[][]yesArray of row data, each containing items
baseSpeednumberβ€”Base scroll speed (default: 0.3)
classNamestringβ€”Additional CSS class for the container
gapnumberβ€”Gap between items in pixels (default: 16)
isActivebooleanβ€”Whether the animation is active
itemMinWidthnumberβ€”Item minimum width in pixels (default: 180)
renderItem((item: MarqueeItem, index: number) => ReactNode)β€”Custom item render function
rowGapnumberβ€”Gap between rows (default: 8)
speedIncrementnumberβ€”Speed increment per row (default: 0.05)

@getnarro/shared-ui

<AgendaItem>

A styled agenda item component Displays an agenda item with a centered number in bold with a colored underline, followed by descriptive text. Useful for agenda presentations and step-by-step processes.

<AgendaItem number={1} title="Introduction" />
<AgendaItem number={2} title="Main Topic" underlineColor="#00BFA5" />
PropTypeRequiredDescription
numbernumberyesThe number of the agenda item
titlestringyesTitle of the agenda item
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
underlineColorstringβ€”Color of the underline

<AnimatedList>

List with staggered animations Note: Import the animations CSS file to use this component: import β€œ@getnarro/shared-ui/styles/animations.css”;

<AnimatedList
  items={['First point', 'Second point', 'Third point']}
  animation="slide-up"
  staggerDelay={150}
  variant="check"
/>
PropTypeRequiredDescription
itemsReactNode[]yesList items to animate
animationListAnimationTypeβ€”Animation type
classNamestringβ€”Additional CSS classes
durationnumberβ€”Animation duration per item in milliseconds
initialDelaynumberβ€”Delay before starting first animation in milliseconds
isActivebooleanβ€”Override slide active state
orderedbooleanβ€”Ordered or unordered list
staggerDelaynumberβ€”Delay between each item in milliseconds
variant"disc" | "decimal" | "check" | "arrow" | "none"β€”List style variant

<Animation>

A CSS animation wrapper component When used inside a Slide component from narro, animations will only trigger when the slide becomes active. Note: Import the animations CSS file to use this component: import β€œ@getnarro/shared-ui/styles/animations.css”;

<Animation type="slide-up" delay={200} duration={500}>
  <Heading level={1}>Animated Title</Heading>
</Animation>
PropTypeRequiredDescription
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
delaynumberβ€”Animation delay (ms)
durationnumberβ€”Animation duration (ms)
easing"linear" | "ease-out" | "ease-in-out" | "ease" | "ease-in"β€”Animation easing
isActivebooleanβ€”Override slide active state (for use without narro context)
trigger"mount" | "hover" | "visible"β€”Trigger animation on mount
typeAnimationTypeβ€”Animation type

<BigText>

Extremely large, impactful text component

<BigText effect="gradient" animate="blur-reveal">
  IMPACT
</BigText>
PropTypeRequiredDescription
align"center" | "left" | "right"β€”Text alignment
animateBigTextAnimationβ€”Animation preset
animationDurationnumberβ€”Animation duration in milliseconds
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
colorstringβ€”Text color
effectBigTextEffectβ€”Text effect to apply
gradientColorsstringβ€”Custom gradient colors (when effect is β€œgradient”)

<Chart>

A simple chart component This is a basic CSS-based implementation suitable for presentations. For more advanced charts, consider integrating with Chart.js or Recharts.

<Chart
  type="bar"
  data={[
    { label: 'Q1', value: 100 },
    { label: 'Q2', value: 150 },
    { label: 'Q3', value: 200 },
  ]}
  title="Quarterly Sales"
/>
PropTypeRequiredDescription
dataChartDataPoint[]yesChart data
type"line" | "bar" | "pie" | "donut"yesChart type
classNamestringβ€”Additional CSS classes
colorsstring[]β€”Color palette
heightnumberβ€”Chart height
showLegendbooleanβ€”Show legend
titlestringβ€”Chart title

<Code>

A code block component with optional line numbers and copy button Note: This is a basic implementation. For production use, consider integrating with Shiki or Prism for proper syntax highlighting.

<Code language="typescript" lineNumbers>
{`const greeting = "Hello, World!";
console.log(greeting);`}
</Code>
PropTypeRequiredDescription
childrenstringyesCode content
classNamestringβ€”Additional CSS classes
copyButtonbooleanβ€”Show copy button
highlightLinesstringβ€”Lines to highlight (comma-separated or range)
languagestringβ€”Programming language
lineNumbersbooleanβ€”Show line numbers
theme"dark" | "light"β€”Theme variant

<CommentForm>

A form for creating or editing comments

<CommentForm
  slideId="slide-1"
  defaultAuthor="John Doe"
  onSubmit={(comment) => handleAddComment(comment)}
  onCancel={() => setShowForm(false)}
/>
PropTypeRequiredDescription
onSubmit(comment: Omit<SlideComment, "id" | "createdAt">) => voidyesCallback when comment is submitted
slideIdstringyesSlide ID to add comment to
classNamestringβ€”Additional CSS classes
defaultAuthorstringβ€”Default author name
initialContentstringβ€”Initial content (for editing)
isEditingbooleanβ€”Whether this is editing an existing comment
onCancel(() => void)β€”Callback when form is cancelled
parentIdstringβ€”Parent comment ID for replies
placeholderstringβ€”Placeholder text
positionCommentPositionβ€”Position for inline comments

<CommentItem>

Displays a single comment with actions

<CommentItem
  comment={comment}
  onEdit={(id) => handleEdit(id)}
  onDelete={(id) => handleDelete(id)}
  onResolve={(id, resolved) => handleResolve(id, resolved)}
/>
PropTypeRequiredDescription
commentSlideCommentyesThe comment to display
classNamestringβ€”Additional CSS classes
currentUserstringβ€”Current user’s name (to determine if they can edit/delete)
onDelete((commentId: string) => void)β€”Callback when delete is clicked
onEdit((commentId: string) => void)β€”Callback when edit is clicked
onReaction((commentId: string, emoji: string) => void)β€”Callback when a reaction is added
onReply((commentId: string, content: string) => void)β€”Callback when reply is submitted
onResolve((commentId: string, resolved: boolean) => void)β€”Callback when resolve is toggled
repliesSlideComment[]β€”Child comments (replies)
showReplyFormbooleanβ€”Whether to show the reply form

<CommentMarker>

A visual indicator for comment positions on slides

<CommentMarker
  position={{ x: 50, y: 30 }}
  count={3}
  onClick={() => setShowComments(true)}
/>
PropTypeRequiredDescription
positionCommentPositionyesPosition of the marker on the slide (percentage 0-100)
classNamestringβ€”Additional CSS classes
commentIdstringβ€”Comment ID this marker represents
countnumberβ€”Number of comments at this position
isActivebooleanβ€”Whether the marker is in an active/selected state
onClick(() => void)β€”Callback when marker is clicked

<CommentPanel>

A sidebar panel for managing comments

<CommentPanel
  slideId="slide-1"
  comments={allComments}
  onAddComment={(comment) => addComment(comment)}
  onDeleteComment={(id) => deleteComment(id)}
  currentUser="John Doe"
/>
PropTypeRequiredDescription
commentsSlideComment[]yesAll comments data
onAddComment(comment: Omit<SlideComment, "id" | "createdAt">) => voidyesCallback to add a new comment
classNamestringβ€”Additional CSS classes
currentUserstringβ€”Current user name
filterCommentFilterOptionsβ€”Filter options
isCollapsedbooleanβ€”Whether the panel is in collapsed state
onCollapseChange((collapsed: boolean) => void)β€”Callback when collapse state changes
onDeleteComment((commentId: string) => void)β€”Callback to delete a comment
onFilterChange((filter: CommentFilterOptions) => void)β€”Callback when filter changes
onResolveComment((commentId: string, resolved: boolean) => void)β€”Callback to resolve/unresolve a comment
onUpdateComment((commentId: string, updates: Partial<SlideComment>) => void)β€”Callback to update a comment
slideIdstringβ€”Slide ID to show comments for (if undefined, shows all)

<ContentSlide>

A slide with optional title and content area

<ContentSlide title="Key Features">
  <List items={['Feature 1', 'Feature 2', 'Feature 3']} />
</ContentSlide>
PropTypeRequiredDescription
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
titleReactNodeβ€”Slide title

<CountingNumber>

Animated number counter

<CountingNumber
  value={1000000}
  duration={2000}
  format={{ prefix: "$", separator: true }}
/>
PropTypeRequiredDescription
valuenumberyesTarget value to count to
classNamestringβ€”Additional CSS classes
delaynumberβ€”Delay before starting animation in milliseconds
durationnumberβ€”Animation duration in milliseconds
easing"linear" | "ease-out" | "ease-in-out"β€”Animation easing
format{ decimals?: number | undefined; prefix?: string | undefined; suffix?: string | undefined; separator?: boolean | undefined; locale?: string | undefined; }β€”Number format options
fromnumberβ€”Starting value (default: 0)
isActivebooleanβ€”Override slide active state
onComplete(() => void)β€”Callback when animation completes

<Diagram>

A diagram rendering component This is a basic text/pre-formatted implementation. For actual Mermaid support, integrate with the mermaid library: https://mermaid.js.org/

// Text/ASCII diagram
<Diagram type="text">
{`
  +--------+     +--------+
  | Client | --> | Server |
  +--------+     +--------+
`}
</Diagram>

// Mermaid (requires mermaid library integration)
<Diagram type="mermaid">
{`graph LR
  A[Client] --> B[Server]
  B --> C[Database]`}
</Diagram>
PropTypeRequiredDescription
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
theme"dark" | "light" | "forest"β€”Diagram theme
type"text" | "mermaid"β€”Diagram type

<DottedArc>

Decorative concentric dotted arcs Creates a series of concentric arcs with dashed lines in varying colors. Used as a decorative element for title slides and maintaining brand consistency.

<DottedArc
  colors={["#ff6b35", "#4ecdc4", "#1a535c"]}
  position="bottom-right"
/>
// Custom sizing and spacing
<DottedArc
  colors={["orange", "green", "blue"]}
  width={3}
  spacing={10}
  size={400}
  position="bottom-left"
/>
PropTypeRequiredDescription
colorsstring[]yesArray of colors for each arc
classNamestringβ€”Additional CSS classes
position"bottom-right" | "bottom-left" | "top-right" | "top-left"β€”Arc position
sizenumberβ€”Size of the arc container
spacingnumberβ€”Spacing between arcs
widthnumberβ€”Line width for arcs

<DualColumnTextAndImage>

A two-column layout where text is on one side and an image is on the other

<DualColumnTextAndImage
  text="This is the text content"
  image="/path/to/image.png"
  imageAlt="Description of image"
  gap="md"
/>
PropTypeRequiredDescription
imagestringyesURL of the image to display in the right column
textReactNodeyesText content to display in the left column
classNamestringβ€”Additional CSS classes
gap"sm" | "md" | "lg" | "xl"β€”Gap between columns
imageAltstringβ€”Alt text for the image
reversebooleanβ€”Reverse the layout (image left, text right)

<Embed>

External content embedding component Supports YouTube, Vimeo, Google Slides, websites, web applications, and generic iframes.

// Embed a YouTube video
<Embed
  url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  title="Video demonstration"
  aspectRatio="16:9"
/>

// Embed a web application with sandbox restrictions
<Embed
  url="https://codesandbox.io/embed/..."
  title="Live code demo"
  sandbox="allow-scripts allow-same-origin"
  loading="lazy"
/>

// Embed a website
<Embed
  url="https://example.com"
  title="Example website"
  aspectRatio="16:9"
/>
PropTypeRequiredDescription
urlstringyesURL to embed (YouTube, Vimeo, websites, web apps, etc.)
allowstringβ€”Custom permissions for iframe (default includes common permissions)
allowFullscreenbooleanβ€”Allow fullscreen
aspectRatio"1:1" | "16:9" | "4:3"β€”Aspect ratio
classNamestringβ€”Additional CSS classes
loading"lazy" | "eager"β€”Lazy loading strategy
referrerPolicy"no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url"β€”Referrer policy
sandboxstringβ€”Sandbox restrictions for iframe security
titlestringβ€”Embed title for accessibility

<FlowDiagram>

A visual representation of a process flow with steps

<FlowDiagram
  steps={[
    {label: 'Step 1', icon: '/icon1.png', description: 'First step'},
    {label: 'Step 2', icon: '/icon2.png', description: 'Second step'}
  ]}
  direction="horizontal"
  color="#08979c"
/>
PropTypeRequiredDescription
stepsFlowStep[]yesList of steps with labels and optional icons
classNamestringβ€”Additional CSS classes
colorstringβ€”Color for step boxes and connectors
direction"horizontal" | "vertical"β€”Direction of flow
gap"sm" | "md" | "lg" | "xl"β€”Gap between steps

<FourColumn>

A four-column layout component with equal columns

<FourColumn
  first={<Text>Column 1</Text>}
  second={<Text>Column 2</Text>}
  third={<Text>Column 3</Text>}
  fourth={<Text>Column 4</Text>}
  gap="lg"
  align="center"
/>
PropTypeRequiredDescription
firstReactNodeyesFirst column content
fourthReactNodeyesFourth column content
secondReactNodeyesSecond column content
thirdReactNodeyesThird column content
align"center" | "start" | "end" | "stretch"β€”Vertical alignment
classNamestringβ€”Additional CSS classes
gap"sm" | "md" | "lg" | "xl"β€”Gap between columns
responsivebooleanβ€”Stack columns on small screens

<Fragment>

Step-by-step reveal component Each Fragment animates in sequence based on its order prop. The delay is calculated as: order * staggerDelay When used inside a Slide component from narro, animations will only trigger when the slide becomes active. Note: Import the animations CSS file to use this component: import β€œ@getnarro/shared-ui/styles/animations.css”;

<Fragment order={1} effect="fade-in">
  <Text>This appears first (after 400ms)</Text>
</Fragment>
<Fragment order={2} effect="slide-up">
  <Text>This appears second (after 800ms)</Text>
</Fragment>
PropTypeRequiredDescription
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
durationnumberβ€”Animation duration (ms)
effectFragmentEffectβ€”Animation effect
isActivebooleanβ€”Override slide active state (for use without narro context)
ordernumberβ€”Order in which fragment appears
staggerDelaynumberβ€”Delay between each fragment (ms) - used to stagger animations

<Grid>

A flexible grid layout component

<Grid columns={3} gap="md">
  <Card>Item 1</Card>
  <Card>Item 2</Card>
  <Card>Item 3</Card>
</Grid>
PropTypeRequiredDescription
align"center" | "start" | "end" | "stretch"β€”Horizontal alignment
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
columns2 | 3 | 4 | 5 | 6 | 1β€”Number of columns
gap"sm" | "md" | "lg" | "xl"β€”Gap between items
justify"center" | "start" | "end" | "stretch"β€”Vertical alignment

<Heading>

A styled heading component (h1-h6)

<Heading level={1}>Main Title</Heading>
<Heading level={2} color="primary">Subtitle</Heading>
PropTypeRequiredDescription
align"center" | "left" | "right"β€”Text alignment
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
color"default" | "primary" | "muted" | "accent"β€”Color variant
level2 | 3 | 4 | 5 | 6 | 1β€”Heading level (1-6)

<HexPatternBackground>

A background with a hexagonal pattern

<HexPatternBackground backgroundColor="#00d9c4" hexColor="#08979c">
  <h1>Content here</h1>
</HexPatternBackground>
PropTypeRequiredDescription
backgroundColorstringβ€”Background color (hex or rgb)
childrenReactNodeβ€”Content to be rendered on top of the hexagon pattern background
classNamestringβ€”Additional CSS classes
hexColorstringβ€”Hexagon stroke color (hex or rgb)
patternSize"sm" | "md" | "lg"β€”Pattern size (affects hexagon scale)

<Icon>

A simple icon component This is a basic implementation using Unicode symbols and emoji. For production use with custom icons, consider integrating with a proper icon library like Lucide, Heroicons, or Font Awesome.

<Icon name="check" size="lg" color="green" />
<Icon name="github" size={32} />
PropTypeRequiredDescription
namestringyesIcon name or SVG content
classNamestringβ€”Additional CSS classes
colorstringβ€”Icon color
labelstringβ€”Accessibility label
sizenumber | "sm" | "md" | "lg" | "xl"β€”Icon size

<Image>

An optimized image component with filter support

<Image
  src="/images/photo.jpg"
  alt="A beautiful landscape"
  fit="cover"
  rounded="lg"
  shadow
  filter="grayscale"
/>
Custom filter:
```tsx
<Image
  src="/images/photo.jpg"
  alt="Custom filtered"
  customFilter="brightness(80%) contrast(110%)"
/>

```tsx
Color overlay:
```tsx
<Image
  src="/images/photo.jpg"
  alt="Blue tinted"
  overlay="#3b82f6"
  overlayOpacity={0.4}
/>

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `alt` | `string` | yes | Alt text for accessibility |
| `src` | `string` | yes | Image source URL |
| `children` | `ReactNode` | β€” | Children content |
| `className` | `string` | β€” | Additional CSS classes |
| `customFilter` | `string` | β€” | Custom CSS filter value (overrides filter preset) |
| `filter` | `ImageFilter` | β€” | Apply CSS filter preset |
| `fit` | `"none" \| "cover" \| "contain" \| "fill"` | β€” | Object fit mode |
| `height` | `string \| number` | β€” | Height constraint |
| `overlay` | `string` | β€” | Color overlay (CSS color value) |
| `overlayOpacity` | `number` | β€” | Opacity of the color overlay (0-1, default: 0.5) |
| `rounded` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | β€” | Border radius |
| `shadow` | `boolean` | β€” | Add shadow |
| `width` | `string \| number` | β€” | Width constraint |

### `<ImageBackground>`

A full-bleed background image with content overlay

```tsx
<ImageBackground
  src="/images/hero.jpg"
  alt="Hero background"
  overlay="dark"
  kenBurns
>
  <Heading level={1}>Bold Title</Heading>
  <Text>Subtitle text here</Text>
</ImageBackground>
PropTypeRequiredDescription
srcstringyesImage source URL
altstringβ€”Image alt text for accessibility
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
kenBurnsbooleanβ€”Enable Ken Burns animation effect (slow zoom/pan)
kenBurnsDurationnumberβ€”Ken Burns animation duration in seconds
overlayImageBackgroundOverlayβ€”Overlay type for text readability
overlayColorstringβ€”Custom overlay color (CSS color value)
position"center" | "cover" | "contain" | "top" | "bottom"β€”Image positioning

<ImageSlide>

A full-bleed image slide with optional overlay

<ImageSlide
  src="/images/hero.jpg"
  alt="Hero image"
  overlay={<Heading level={1}>Welcome</Heading>}
  overlayPosition="center"
/>
PropTypeRequiredDescription
srcstringyesImage source URL
altstringβ€”Image alt text
classNamestringβ€”Additional CSS classes
fit"cover" | "contain" | "fill"β€”Object fit mode
overlayReactNodeβ€”Overlay content
overlayPosition"center" | "top" | "bottom"β€”Overlay position

<ImageSpotlight>

Keynote-style Magic Move for images Creates smooth zoom and pan animations between slides. Elements with the same spotlightId will animate smoothly when transitioning between slides.

// Slide 1 - Overview (centered, no zoom)
<ImageSpotlight
  spotlightId="diagram"
  src="/architecture.svg"
  alt="Architecture diagram"
  x={50}
  y={50}
  scale={1}
  width="100%"
  height={400}
/>

// Slide 2 - Zoom to top-left element
<ImageSpotlight
  spotlightId="diagram"
  src="/architecture.svg"
  alt="Architecture diagram"
  x={25}
  y={25}
  scale={2.5}
  width="100%"
  height={400}
/>

// Slide 3 - Pan to bottom-right element
<ImageSpotlight
  spotlightId="diagram"
  src="/architecture.svg"
  alt="Architecture diagram"
  x={75}
  y={75}
  scale={2.5}
  width="100%"
  height={400}
/>
PropTypeRequiredDescription
altstringyesAlt text for accessibility
spotlightIdstringyesUnique identifier for cross-slide morphing - elements with same id will animate between slides
srcstringyesImage source URL
classNamestringβ€”Additional CSS classes for container
durationnumberβ€”Animation duration in ms, default 800
easing"linear" | "ease-out" | "ease-in-out" | "ease" | "ease-in" | "spring"β€”Animation easing function
heightstring | numberβ€”Container height (CSS value or number in px)
imageClassNamestringβ€”Additional CSS classes for image
onAnimationComplete(() => void)β€”Callback when animation completes
overlayReactNodeβ€”Optional overlay content that appears on top
rounded"none" | "sm" | "md" | "lg" | "xl" | "full"β€”Border radius
scalenumberβ€”Scale/zoom level, default 1
shadowbooleanβ€”Show shadow
widthstring | numberβ€”Container width (CSS value or number in px)
xnumberβ€”X position as percentage (0-100), default 50 (centered)
ynumberβ€”Y position as percentage (0-100), default 50 (centered)

<InfoCardWithIcon>

A styled information card with icon Displays an icon on the left with bold headings and descriptive text to the right. Uses consistent alignment and spacing. Perfect for concept highlights and important points.

<InfoCardWithIcon
  iconSrc="/icon.svg"
  title="Key Feature"
  description="This is an important concept to highlight"
/>
PropTypeRequiredDescription
descriptionstringyesDescription text explaining the icon
iconSrcstringyesURL for the icon image
titlestringyesTitle of the info card
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes

<List>

An animated list component

<List
  items={['First point', 'Second point', 'Third point']}
  variant="check"
  animated
/>
PropTypeRequiredDescription
itemsReactNode[]yesList items
animatedbooleanβ€”Enable animations
animationDelaynumberβ€”Animation delay between items (ms)
classNamestringβ€”Additional CSS classes
orderedbooleanβ€”Ordered or unordered list
variant"disc" | "decimal" | "check" | "arrow" | "none"β€”List style

<LogoGrid>

A grid layout for displaying multiple logos with even spacing

<LogoGrid
  logos={[
    {src: '/path/to/logo1.png', name: 'Company 1'},
    {src: '/path/to/logo2.png', name: 'Company 2'}
  ]}
  columns={3}
  gap="md"
/>
PropTypeRequiredDescription
logosLogo[]yesArray of logo objects with source and alt text.
classNamestringβ€”Additional CSS classes
columns2 | 3 | 4 | 5 | 6β€”Number of columns
gap"sm" | "md" | "lg" | "xl"β€”Gap between logos

<MorphElement>

Element that morphs across slides using View Transitions API When elements with the same transitionName exist on consecutive slides, they will smoothly morph into each other during slide transitions.

// Slide 1
<MorphElement transitionName="hero-image">
  <img src="product.jpg" style={{ width: 200 }} />
</MorphElement>

// Slide 2 - same transitionName, different size
<MorphElement transitionName="hero-image">
  <img src="product.jpg" style={{ width: "100%" }} />
</MorphElement>
PropTypeRequiredDescription
childrenReactNodeyesElement content
transitionNamestringyesUnique name for cross-slide morphing
asElementType<any, keyof IntrinsicElements>β€”HTML tag to render
classNamestringβ€”Additional CSS classes
styleCSSPropertiesβ€”Additional inline styles

<ProcessFlowChart>

Interactive flowchart for process visualization Creates a flowchart with customizable nodes and connections. Nodes are automatically laid out based on the direction prop, and connections are drawn between them with arrows.

<ProcessFlowChart
  nodes={[
    { id: "start", label: "Start", color: "#4ecdc4" },
    { id: "process", label: "Process", color: "#ff6b35" },
    { id: "end", label: "End", color: "#95e1d3" }
  ]}
  connections={[
    { from: "start", to: "process" },
    { from: "process", to: "end" }
  ]}
  direction="horizontal"
/>
// Vertical flow with custom styling
<ProcessFlowChart
  nodes={[
    { id: "1", label: "Input", color: "#1a535c", textColor: "#fff" },
    { id: "2", label: "Transform", color: "#4ecdc4" },
    { id: "3", label: "Output", color: "#ff6b35", textColor: "#fff" }
  ]}
  connections={[
    { from: "1", to: "2", label: "data" },
    { from: "2", to: "3", label: "result" }
  ]}
  direction="vertical"
  nodeGap="lg"
  arrowStyle="dashed"
/>
PropTypeRequiredDescription
connectionsProcessFlowConnection[]yesArray of connections between node ids
nodesProcessFlowNode[]yesArray of nodes with labels and colors
arrowStyle"solid" | "dashed"β€”Arrow style for connections
classNamestringβ€”Additional CSS classes
direction"horizontal" | "vertical"β€”Orientation of the flow
nodeGap"sm" | "md" | "lg"β€”Gap between nodes

<ProfileCard>

A styled profile card component Displays a profile image, name, title, and associated company icons. Perfect for team introductions and speaker introductions.

<ProfileCard
  imageSrc="/avatar.jpg"
  name="John Doe"
  title="CEO & Founder"
  companyIcons={["/company1.svg", "/company2.svg"]}
/>
PropTypeRequiredDescription
imageSrcstringyesSource URL for the profile image
namestringyesName of the individual
titlestringyesTitle or role of the individual
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
companyIconsstring[]β€”Array of icon URLs to display

<ProgressRing>

Animated circular progress indicator

<ProgressRing value={75} size={120} showValue />
PropTypeRequiredDescription
valuenumberyesProgress value (0-100)
backgroundColorstringβ€”Background ring color
childrenReactNodeβ€”Custom center content
classNamestringβ€”Additional CSS classes
colorstringβ€”Ring color
delaynumberβ€”Delay before starting animation in milliseconds
durationnumberβ€”Animation duration in milliseconds
isActivebooleanβ€”Override slide active state
onComplete(() => void)β€”Callback when animation completes
showValuebooleanβ€”Show percentage text in center
sizenumberβ€”Size of the ring in pixels
strokeWidthnumberβ€”Stroke width in pixels

<Quote>

A styled blockquote component

<Quote
  author="Albert Einstein"
  source="Interview, 1929"
>
  "Imagination is more important than knowledge."
</Quote>
PropTypeRequiredDescription
authorstringβ€”Quote author
authorStyleCSSPropertiesβ€”Custom style for the author/figcaption
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
sourcestringβ€”Source or reference
variant"default" | "large" | "minimal"β€”Visual variant

<ScrollableImage>

Displays a scrollable image Perfect for displaying full-page website screenshots that are too tall to fit in a slide.

// Basic scrollable image
<ScrollableImage
  src="/screenshots/long-page.png"
  alt="Full page screenshot"
  maxHeight="70vh"
/>

// With auto-scroll
<ScrollableImage
  src="/screenshots/website.png"
  alt="Website screenshot"
  maxHeight="70vh"
  autoScrollSpeed={50}
  showIndicators
/>
PropTypeRequiredDescription
altstringyesAlt text for accessibility
srcstringyesImage source URL or path
autoScrollSpeednumberβ€”Auto-scroll speed in pixels per second (0 to disable)
classNamestringβ€”Additional CSS classes
maxHeightstringβ€”Maximum height of the container
showIndicatorsbooleanβ€”Show scroll indicators
smoothScrollbooleanβ€”Enable smooth scrolling

<ScrollableImage>

Displays a scrollable image Perfect for displaying full-page website screenshots that are too tall to fit in a slide.

// Basic scrollable image
<ScrollableImage
  src="/screenshots/long-page.png"
  alt="Full page screenshot"
  maxHeight="70vh"
/>

// With auto-scroll
<ScrollableImage
  src="/screenshots/website.png"
  alt="Website screenshot"
  maxHeight="70vh"
  autoScrollSpeed={50}
  showIndicators
/>
PropTypeRequiredDescription
altstringyesAlt text for accessibility
srcstringyesImage source URL or path
autoScrollSpeednumberβ€”Auto-scroll speed in pixels per second (0 to disable)
classNamestringβ€”Additional CSS classes
maxHeightstringβ€”Maximum height of the container
showIndicatorsbooleanβ€”Show scroll indicators
smoothScrollbooleanβ€”Enable smooth scrolling

<Text>

A styled text block component

<Text size="lg" weight="medium">
  This is styled body text for your slides.
</Text>
PropTypeRequiredDescription
align"center" | "left" | "right" | "justify"β€”Text alignment
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
color"default" | "primary" | "muted" | "accent"β€”Color variant
size"sm" | "md" | "lg" | "xl" | "2xl"β€”Text size
weight"medium" | "normal" | "semibold" | "bold"β€”Font weight

<ThreeColumn>

A three-column layout component

<ThreeColumn
  first={<Text>First column</Text>}
  second={<Text>Second column</Text>}
  third={<Image src="image.jpg" alt="Third column" />}
  ratio="1:2:1"
  gap="lg"
/>
PropTypeRequiredDescription
firstReactNodeyesFirst column content
secondReactNodeyesSecond column content
thirdReactNodeyesThird column content
align"center" | "start" | "end" | "stretch"β€”Vertical alignment
classNamestringβ€”Additional CSS classes
gap"sm" | "md" | "lg" | "xl"β€”Gap between columns
ratio"1:1:1" | "2:1:1" | "1:2:1" | "1:1:2" | "2:2:1" | "1:2:2"β€”Column ratio (e.g., β€œ1:1:1”, β€œ2:1:1”, β€œ1:2:1”)
responsivebooleanβ€”Stack columns on small screens

<TimelineWithIndicators>

A horizontal timeline component Creates a horizontal timeline with circular indicators linked by a line. Includes labels below each indicator. Perfect for historical overviews and progress tracking.

<TimelineWithIndicators
  events={[
    { year: "2020", label: "Founded" },
    { year: "2021", label: "Series A" },
    { year: "2022", label: "Expansion" },
  ]}
  lineColor="#14b8a6"
/>
PropTypeRequiredDescription
eventsTimelineEvent[]yesArray of event objects with year and label
classNamestringβ€”Additional CSS classes
lineColorstringβ€”Color of the connecting line

<TitleSlide>

A centered title slide layout

<TitleSlide
  title="Welcome to My Presentation"
  subtitle="An Overview of Our Progress"
  author="John Doe"
  date="January 2025"
/>
PropTypeRequiredDescription
titleReactNodeyesMain title
authorReactNodeβ€”Author or presenter name
classNamestringβ€”Additional CSS classes
dateReactNodeβ€”Date or additional info
subtitleReactNodeβ€”Subtitle

<TitleSlideWithElement>

A title slide with a flexible element area

<TitleSlideWithElement
  title="Product Launch"
  subtitle="Introducing our newest innovation"
  element={<Image src="product.png" alt="Product" />}
  elementPosition="right"
  elementAnimation="fade-in"
/>
PropTypeRequiredDescription
elementReactNodeyesElement to display (image, icon, chart, etc.)
titleReactNodeyesMain title
animationDelaynumberβ€”Animation delay in milliseconds
classNamestringβ€”Additional CSS classes
elementAnimationElementAnimationβ€”Animation preset for the element
elementPositionElementPositionβ€”Position of the element relative to title/subtitle
subtitleReactNodeβ€”Subtitle

<TwoColumn>

A two-column layout component

<TwoColumn
  left={<Text>Left content</Text>}
  right={<Image src="image.jpg" alt="Right image" />}
  ratio="2:1"
/>
PropTypeRequiredDescription
leftReactNodeyesLeft column content
rightReactNodeyesRight column content
align"center" | "start" | "end" | "stretch"β€”Vertical alignment
classNamestringβ€”Additional CSS classes
gap"sm" | "md" | "lg" | "xl"β€”Gap between columns
ratio"1:1" | "2:1" | "1:2" | "3:2" | "2:3"β€”Column ratio

<TwoColumnHighlight>

Two-column layout with highlighted sections A layout component featuring two columns with different backgrounds, typically used for section headers or emphasizing key points. The left column is typically darker with a narrower width.

<TwoColumnHighlight
  leftContent={<Heading level={1}>Section Title</Heading>}
  rightContent={<Text>Description text goes here</Text>}
  ratio="1:2"
/>
// With custom colors
<TwoColumnHighlight
  leftContent="Key Point"
  rightContent="Detailed explanation"
  leftBg="#000000"
  rightBg="#ffffff"
  leftTextColor="#ffffff"
  rightTextColor="#000000"
  highlightColor="#ff6b35"
/>
PropTypeRequiredDescription
leftContentReactNodeyesContent for the left column
rightContentReactNodeyesContent for the right column
classNamestringβ€”Additional CSS classes
highlightstringβ€”Text to highlight in the right column
highlightColorstringβ€”Highlight color for emphasized text
leftBgstringβ€”Left column background color
leftTextColorstringβ€”Left column text color
ratio"1:2" | "2:3" | "1:3"β€”Column width ratio (left:right)
rightBgstringβ€”Right column background color
rightTextColorstringβ€”Right column text color

<TypewriterText>

Text that appears character by character

<TypewriterText speed="medium" cursor>
  Hello, World!
</TypewriterText>
PropTypeRequiredDescription
childrenstringyesText content to animate
classNamestringβ€”Additional CSS classes
cursorbooleanβ€”Show blinking cursor
delaynumberβ€”Delay before starting animation in milliseconds
isActivebooleanβ€”Override slide active state
onComplete(() => void)β€”Callback when animation completes
speednumber | "slow" | "medium" | "fast"β€”Speed of typing in milliseconds per character

<Video>

A video embedding component

<Video
  src="/videos/demo.mp4"
  poster="/images/poster.jpg"
  title="Product demo video"
  autoPlay
  loop
  muted
/>
PropTypeRequiredDescription
srcstringyesVideo source URL
autoPlaybooleanβ€”Autoplay video
classNamestringβ€”Additional CSS classes
controlsbooleanβ€”Show controls
fit"cover" | "contain" | "fill"β€”Object fit mode
loopbooleanβ€”Loop video
mutedbooleanβ€”Mute video
posterstringβ€”Poster image
titlestringβ€”Accessible title/description for the video

<WaveLine>

A decorative curved dashed line component Creates a smooth, curved dashed line that spans across a slide to add dynamic visual elements. Perfect for title slide decoration and section separators.

<WaveLine />
<WaveLine color="#00BFA5" dashWidth={3} />
PropTypeRequiredDescription
childrenReactNodeβ€”Children content
classNamestringβ€”Additional CSS classes
colorstringβ€”Color of the dashed line
dashWidthnumberβ€”Width of each dash

<WordReveal>

Text that reveals word by word Note: Import the animations CSS file to use this component: import β€œ@getnarro/shared-ui/styles/animations.css”;

<WordReveal animation="blur" staggerDelay={100}>
  This text will appear word by word
</WordReveal>
PropTypeRequiredDescription
childrenstringyesText content to animate (will be split by words)
animationWordRevealAnimationβ€”Animation type
classNamestringβ€”Additional CSS classes
delaynumberβ€”Delay before starting animation in milliseconds
durationnumberβ€”Animation duration per word in milliseconds
isActivebooleanβ€”Override slide active state
onComplete(() => void)β€”Callback when all words are revealed
staggerDelaynumberβ€”Delay between each word in milliseconds

<Workflow>

Animated workflow diagram Note: Import the animations CSS file to use this component: import β€œ@getnarro/shared-ui/styles/animations.css”;

<Workflow animation="progressive" direction="horizontal">
  <WorkflowStep icon="1" title="Start">Begin here</WorkflowStep>
  <WorkflowStep icon="2" title="Process">Do the work</WorkflowStep>
  <WorkflowStep icon="3" title="Complete">All done!</WorkflowStep>
</Workflow>
PropTypeRequiredDescription
childrenReactNodeyesWorkflowStep children
animationWorkflowAnimationβ€”Animation type
classNamestringβ€”Additional CSS classes
connectorColorstringβ€”Connector line color
directionWorkflowDirectionβ€”Layout direction
highlightActivebooleanβ€”Highlight the currently active step
isActivebooleanβ€”Override slide active state
showConnectorsbooleanβ€”Show connecting lines/arrows between steps
staggerDelaynumberβ€”Delay between each step in milliseconds

<WorkflowStep>

Individual step in a workflow

PropTypeRequiredDescription
childrenReactNodeyesStep content
classNamestringβ€”Additional CSS classes
descriptionstringβ€”Step description
iconReactNodeβ€”Icon or number to display
titlestringβ€”Step title

<ZoomImage>

Image with click-to-zoom functionality

<ZoomImage
  src="/diagram.png"
  alt="Architecture diagram"
  zoomable
  zoomScale={2}
/>
PropTypeRequiredDescription
altstringyesAlt text for accessibility
srcstringyesImage source URL
classNamestringβ€”Additional CSS classes
fit"none" | "cover" | "contain" | "fill"β€”Object fit mode
heightstring | numberβ€”Height constraint
rounded"none" | "sm" | "md" | "lg" | "full"β€”Border radius
shadowbooleanβ€”Add shadow
widthstring | numberβ€”Width constraint
zoomablebooleanβ€”Enable click-to-zoom
zoomScalenumberβ€”Zoom scale factor