> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Component patterns

> How to structure React components in a vly project — composition, reusability, prop drilling vs. context, when to extract.

vly's React structure follows mainstream patterns. This page is the cheat sheet for the conventions worth following.

## File and folder structure

```
app/
├── (auth)/
│   ├── sign-in/page.tsx
│   └── sign-up/page.tsx
├── (app)/
│   ├── layout.tsx                # auth-protected layout
│   ├── page.tsx                  # home
│   ├── tasks/
│   │   ├── page.tsx
│   │   └── [id]/page.tsx
│   └── settings/
└── components/
    ├── ui/                       # shadcn primitives
    ├── tasks/                    # domain-specific
    │   ├── TaskRow.tsx
    │   └── TaskForm.tsx
    └── shared/                   # cross-domain
        ├── Header.tsx
        └── Sidebar.tsx
```

## Composition over duplication

When you find yourself copying JSX, extract:

```tsx theme={null}
// before — duplication
<button className="btn-primary">Save</button>
<button className="btn-primary">Submit</button>

// after — composition
<Button>Save</Button>
<Button>Submit</Button>
```

Use shadcn primitives (`<Button>`, `<Card>`, `<Input>`) for UI atoms; extract domain-specific components (`<TaskRow>`, `<UserAvatar>`) into `components/{domain}/`.

## Prop drilling vs. context

Three-deep prop passing is fine. Five-deep is the point to consider context.

<CardGroup cols={2}>
  <Card title="Use props for data" icon="arrow-right">
    Anything that changes per render — list items, form values, IDs.
  </Card>

  <Card title="Use context for ambient state" icon="globe">
    Theme, current user, active workspace. Things that rarely change but are read everywhere.
  </Card>

  <Card title="Use Convex queries for server state" icon="database">
    Don't hand-roll a server-state context. `useQuery` is the cache.
  </Card>

  <Card title="Use Zustand for ephemeral UI state" icon="layers">
    Modal open/closed, sidebar collapse, multi-step form draft. State that doesn't need to live in the URL or DB.
  </Card>
</CardGroup>

## When to extract

* **3+ uses of the same JSX**: extract a component.
* **5+ lines of JSX inside a JSX expression**: probably extract.
* **A `useEffect` with non-trivial logic**: extract a custom hook.

## Naming

* Components: `PascalCase`. Files match the component (`TaskRow.tsx` exports `TaskRow`).
* Hooks: `useCamelCase`. Files match (`useDebouncedValue.ts`).
* Utilities: `camelCase`. Files often grouped (`utils/format.ts`).

## Tips

<Tip>
  **Start with a single file, extract when needed.** Premature extraction is the most common mistake. A 200-line component is fine if it's read together.
</Tip>

<Tip>
  **Use shadcn slots and composition.** Most "I need a custom button" situations are solved with a Button variant, not a new component.
</Tip>

## Related

<CardGroup cols={3}>
  <Card title="State management" icon="layers" href="/architecture/state-management">
    Where to put state.
  </Card>

  <Card title="shadcn/ui components" icon="boxes" href="/features/design/shadcn-components">
    The primitive library.
  </Card>

  <Card title="Components panel" icon="puzzle" href="/building/components-panel">
    Browse and reuse.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.