orn-ui
Organism

Slides

Requires <UIProvider> — Getting Started →

Installation

Install just this component (copies the source into your project, no npm dependency)

npx orn-ui add slides

Or install the whole package and import it

pnpm add orn-ui
import { Slides } from 'orn-ui/slides';
Depends on:core (theme + icons)transitiongradient

Usage

  • when to use — Onboarding, a feature tour, a carousel. Anything that is one full panel at a time rather than a list.
  • data / keyExtractor / renderItem — Generic over the item type, like a list: the slide content is yours to render.
  • background — Per slide: one colour paints flat, several become a Gradient behind the content.
  • autoPlay / interval — Advances on its own and pauses while a finger is dragging. showAutoPlayToggle is on by default because content that moves on its own needs a way to stop it (WCAG 2.2.2).
  • indicators / indicatorPlacement — Dots or numbers, laid over the slide in white or outside it in theme colours.
  • index / defaultIndex / onIndexChange — Controlled or uncontrolled, the same pair as the rest of the library.
Demo clip not recorded yet — see MEDIA.md

Variants

horizontal · gradient · dots · infinite + autoPlay

<View style={STAGE}>
  <Slides
    data={SLIDES}
    keyExtractor={(item) => item.id}
    background={(item) => item.gradient}
    renderItem={(item) => <Panel slide={item} />}
    loop
    autoPlay
    interval={2500}
  />
</View>

numbered indicators · flat colors · finite · button inside

<View style={{ width: '100%', gap: 8 }}>
  <View style={STAGE}>
    <Slides
      data={SLIDES}
      keyExtractor={(item) => item.id}
      background={(item, index) => FLAT[index]}
      renderItem={(item) => <Panel slide={item} onPress={() => setTapped(item.title)} />}
      indicators="numbers"
    />
  </View>
  <Body>{tapped ? `pressed: ${tapped}` : 'press the button inside a slide'}</Body>
</View>

vertical orientation

<View style={STAGE}>
  <Slides
    data={SLIDES}
    keyExtractor={(item) => item.id}
    background={(item) => item.gradient}
    renderItem={(item) => <Panel slide={item} />}
    orientation="vertical"
  />
</View>
Full demo source
const variants: VariantDef[] = [
  {
    label: 'horizontal · gradient · dots · infinite + autoPlay',
    content: (
      <View style={STAGE}>
        <Slides
          data={SLIDES}
          keyExtractor={(item) => item.id}
          background={(item) => item.gradient}
          renderItem={(item) => <Panel slide={item} />}
          loop
          autoPlay
          interval={2500}
        />
      </View>
    ),
  },
  {
    label: 'numbered indicators · flat colors · finite · button inside',
    content: (
      <View style={{ width: '100%', gap: 8 }}>
        <View style={STAGE}>
          <Slides
            data={SLIDES}
            keyExtractor={(item) => item.id}
            background={(item, index) => FLAT[index]}
            renderItem={(item) => <Panel slide={item} onPress={() => setTapped(item.title)} />}
            indicators="numbers"
          />
        </View>
        <Body>{tapped ? `pressed: ${tapped}` : 'press the button inside a slide'}</Body>
      </View>
    ),
  },
  {
    label: 'vertical orientation',
    content: (
      <View style={STAGE}>
        <Slides
          data={SLIDES}
          keyExtractor={(item) => item.id}
          background={(item) => item.gradient}
          renderItem={(item) => <Panel slide={item} />}
          orientation="vertical"
        />
      </View>
    ),
  },
];

Props

Also accepts testID, forwarded to the root node. It exists for end-to-end tests (Maestro drives the app by the accessibility tree) and has no effect on how the component looks or behaves.

NameTypeDefaultDescription
dataT[]
keyExtractor(item: T, index: number) => string
renderItem(item: T, index: number) => ReactNodeSlide content: images, buttons, whatever. Drawn on top of `background`.
background?(item: T, index: number) => SlideBackgroundBackground per slide: `'#101014'` paints flat, `['#004cef', '#00cae1']` makes a gradient.
gradientDirection?'vertical' | 'horizontal' | 'diagonal' | 'diagonal-reverse'verticalDirection of the gradient when `background` returns several colours.
orientation?'vertical' | 'horizontal'horizontal
indicators?'dots' | 'numbers' | 'none'dotsCircles, numbered circles, or none.
indicatorPlacement?'overlay' | 'outside'overlay'overlay' rests them on the slide, in white; 'outside' moves them out of the swipeable area, in theme colours.
loop?booleanfalseAfter the last one it goes back to the first (and the other way round).
autoPlay?booleanfalseAdvances on its own. Pauses while a finger is dragging.
interval?number4000ms between `autoPlay` advances.
showAutoPlayToggle?booleantrueButton to stop the automatic advance. Only appears with `autoPlay`, and defaults to `true` because content that moves on its own needs a way to pause it (WCAG 2.2.2).
spacing?number0Gap between slides, in px. At 0 they sit flush.
index?numberControlled index. Without it, the component keeps its own starting from `defaultIndex`.
defaultIndex?number0
onIndexChange?(index: number) => void
height?number240Height of the swipeable area.
swipeEnabled?booleantrue
slideStyle?StyleProp<ViewStyle>Styles for the visible box of each slide — padding, alignment, radius.
style?StyleProp<ViewStyle>