N/A

FeatureSplit

Render alternating split feature sections with app-owned media and icon slots.

Feature split sections

A complete split layout for product stories with app-owned visuals.

Compose sections from product-owned content

Render a full split layout while your app keeps ownership of copy, media, icons, routing, and analytics.

Slot-owned media
Bring screenshots, videos, SVGs, or custom cards without coupling the block to one asset pipeline.
Display-ready features
Pass the exact bullets your product needs from static data, CMS records, or localized strings.

Media slot

Compose sections from product-owned content

Alternate the story without rebuilding the layout

Use the same API for product walkthroughs, docs callouts, and marketing proof sections.

Portable by default
No Nuxt Icon strings, no local placeholder imports, and no runtime data fetching.
Responsive split rhythm
The section stacks naturally on mobile and switches to two columns on larger screens.

Reversed layout

Alternate the story without rebuilding the layout

Installation

pnpm dlx shadcn-vue@latest add "https://ui.stackhacker.io/r/feature-split.json"

Usage

<script setup lang="ts">
import type { Component } from 'vue'
import { Boxes, PanelsTopLeft } from 'lucide-vue-next'
import { FeatureSplit, type FeatureSplitFeature, type FeatureSplitSection } from '@/components/feature-split'

const sections: FeatureSplitSection[] = [
  {
    id: 'compose',
    title: 'Compose product stories',
    description: 'Render split layouts while your app owns media and icons.',
    features: [
      { id: 'slots', title: 'Slot-owned media', description: 'Bring screenshots, cards, videos, or custom SVGs.' },
      { id: 'features', title: 'Display-ready features', description: 'Map CMS records or static copy into a portable API.' }
    ]
  }
]

const icons: Partial<Record<string, Component>> = {
  slots: PanelsTopLeft,
  features: Boxes
}

function featureIcon(feature: FeatureSplitFeature) {
  return feature.id ? icons[feature.id] : undefined
}
</script>

<template>
  <FeatureSplit :sections="sections">
    <template #icon="{ feature }">
      <component v-if="featureIcon(feature)" :is="featureIcon(feature)" class="size-5" aria-hidden="true" />
    </template>

    <template #media="{ section }">
      <div class="rounded-2xl border bg-muted p-8">
        {{ section.title }} media
      </div>
    </template>
  </FeatureSplit>
</template>

App-Owned Media and Icons

FeatureSplit owns the responsive split-section layout. Your app owns screenshots, videos, custom art, icon packages, routing, analytics, localization, and content sources.

Pass media through the scoped media slot and feature icons through the scoped icon slot. The component intentionally does not accept Nuxt Icon string names or import local placeholder components.

Examples

Default

Feature split sections

A complete split layout for product stories with app-owned visuals.

Compose sections from product-owned content

Render a full split layout while your app keeps ownership of copy, media, icons, routing, and analytics.

Slot-owned media
Bring screenshots, videos, SVGs, or custom cards without coupling the block to one asset pipeline.
Display-ready features
Pass the exact bullets your product needs from static data, CMS records, or localized strings.

Media slot

Compose sections from product-owned content

Alternate the story without rebuilding the layout

Use the same API for product walkthroughs, docs callouts, and marketing proof sections.

Portable by default
No Nuxt Icon strings, no local placeholder imports, and no runtime data fetching.
Responsive split rhythm
The section stacks naturally on mobile and switches to two columns on larger screens.

Reversed layout

Alternate the story without rebuilding the layout

API Reference

Props

PropTypeDefaultDescription
titlestringOptional group title shown above all sections.
descriptionstringOptional group description shown above all sections.
sectionsFeatureSplitSection[] | null[]Split sections supplied by your app.
classstringAdditional CSS classes for the section group.

Types

interface FeatureSplitFeature {
  id?: string
  title: string
  description?: string
}

interface FeatureSplitSection {
  id?: string
  title: string
  description?: string
  reverse?: boolean
  features?: FeatureSplitFeature[] | null
}

Slots

SlotPropsDescription
icon{ feature, featureIndex, section, sectionIndex }Optional icon markup rendered beside each feature.
media{ section, sectionIndex }Optional media panel rendered beside section copy.