# Onboarding feed 01

> Accordion-style onboarding checklist with per-step CTAs, completion state, and navigation.

A shadcn/ui block from the [@shadcn-studio](https://shadcnstudio.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-studio/onboarding-feed-01).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-studio/onboarding-feed-01
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-studio/onboarding-feed-01-light-840d35381de72d6f.png
- Registry item: https://shadcnstudio.com/r/new-york-v4/onboarding-feed-01.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnstudio.com/r/new-york-v4/onboarding-feed-01.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`
- Registry items: `card`, `accordion`, `button`

## Files

### `src/registry/new-york/blocks/dashboard-and-application/onboarding-feed/onboarding-feed-01/page.tsx`

```tsx
import OnboardingFeed from '@/registry/new-york/components/blocks/onboarding-feed-01/onboarding-feed-01'

const OnboardingFeedPage = () => {
  return (
    <div className='py-8 sm:py-16 lg:py-24'>
      <div className='mx-auto max-w-7xl px-4 sm:px-6 lg:px-8'>
        <div className='flex justify-center'>
          <OnboardingFeed />
        </div>
      </div>
    </div>
  )
}

export default OnboardingFeedPage
```

### `src/registry/new-york/components/blocks/onboarding-feed-01/onboarding-feed-01.tsx`

```tsx
'use client'

import { useState } from 'react'

import { CircleDashedIcon, LogIn, ImportIcon, PlusIcon, CircleCheckIcon } from 'lucide-react'

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/registry/new-york/ui/card'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/registry/new-york/ui/accordion'
import { Button } from '@/registry/new-york/ui/button'

const items = [
  {
    title: 'Sign up and create an account',
    content: `To get started, log in with your organization account from your company.`,
    icon: LogIn,
    label: 'Sign up'
  },
  {
    title: 'Import your data',
    content: 'Connect your database to the new workspace by using one of 20+ database connectors.',
    icon: ImportIcon,
    label: 'Import'
  },
  {
    title: 'Create your first report',
    content: 'Use our drag-and-drop report builder to create your first report and share it with your team.',
    icon: PlusIcon,
    label: 'Create'
  }
]

function OnboardingFeed() {
  const [active, setActive] = useState<string>('item-1')
  const [completed, setCompleted] = useState<Set<number>>(new Set())

  const handleOpenChange = (val: string) => {
    setActive(val)
  }

  const handleComplete = (index: number) => {
    setCompleted(prev => {
      const next = new Set(prev)

      next.add(index)

      return next
    })

    const nextIndex = index + 1

    if (nextIndex < items.length) {
      setActive(`item-${nextIndex + 1}`)
    } else {
      setActive('')
    }
  }

  return (
    <Card className='w-full max-w-lg'>
      <CardHeader>
        <CardTitle>Hello, John !</CardTitle>
        <CardDescription>Let&apos;s set up your first data workspace</CardDescription>
      </CardHeader>
      <CardContent className='space-y-4 text-sm'>
        <Accordion
          type='single'
          collapsible
          className='w-full space-y-2'
          value={active}
          onValueChange={handleOpenChange}
        >
          {items.map((item, index) => {
            const Icon = item.icon
            const isCompleted = completed.has(index)

            return (
              <AccordionItem key={index} value={`item-${index + 1}`} className='rounded-md border!'>
                <AccordionTrigger className='px-5'>
                  <span className='flex items-center gap-2'>
                    {isCompleted ? (
                      <CircleCheckIcon className='size-4 shrink-0' />
                    ) : (
                      <CircleDashedIcon className='size-4 shrink-0' />
                    )}
                    <span>{item.title}</span>
                  </span>
                </AccordionTrigger>
                <AccordionContent className='text-muted-foreground flex flex-col items-start gap-2 px-5'>
                  {item.content}
                  <Button
                    size='sm'
                    onClick={() => handleComplete(index)}
                    disabled={isCompleted || !(index === 0 || completed.has(index - 1))}
                  >
                    <Icon className='size-4 shrink-0' />
                    {item.label}
                  </Button>
                </AccordionContent>
              </AccordionItem>
            )
          })}
        </Accordion>
        <div className='flex justify-end gap-2'>
          <Button className='flex-1 max-sm:w-full' variant='outline'>
            Cancel
          </Button>
          <Button className='flex-1 max-sm:w-full' type='submit'>
            Continue
          </Button>
        </div>
      </CardContent>
    </Card>
  )
}

export default OnboardingFeed
```
