---
title: "Extract Button Component"
description: "Extract the Button component from the home page into the shared UI package and add support for multiple style variants."
canonical_url: "https://vercel.com/academy/production-monorepos/extract-button-component"
md_url: "https://vercel.com/academy/production-monorepos/extract-button-component.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T10:56:17.209Z"
content_type: "lesson"
course: "production-monorepos"
course_title: "Production Monorepos with Turborepo"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Extract Button Component

# Extract button component

You extracted the `Card` component manually to learn the pattern. Now you'll apply that same pattern to extract the `Button` from the home page. This time, you'll add a `variant` prop to support multiple button styles (primary and secondary), making the component more flexible and reusable across different contexts.

## Outcome

Extract the `Button` component to the shared package with support for multiple style variants.

## Fast track

1. Create `Button` component in `packages/ui` with variant support
2. Export `Button` from the package
3. Update home page to use the shared `Button`
4. Test both primary and secondary variants

## Hands-on exercise 2.4

Extract `Button` component with variant support using the same pattern you learned with `Card`.

**Requirements:**

1. Create `Button` component in `packages/ui/src/button.tsx` with variant prop
2. Export `Button` from package (package.json + index.ts)
3. Update home page to use `<Button>` from shared package
4. Test both primary and secondary variants

**Implementation hints:**

- `Button` supports primary (blue) and secondary (gray) variants
- The variant prop defaults to 'primary'
- Use the same export pattern as `Card` (package.json exports field + index.ts)

**Files to create/modify:**

- `packages/ui/src/button.tsx` (new component)
- `packages/ui/package.json` (add Button export)
- `packages/ui/src/index.ts` (export Button)
- `apps/web/app/page.tsx` (use Button from shared package)

## Extract button component

The home page has an inline button. Let's extract it to the UI package.

Create `packages/ui/src/button.tsx` with the Button code:

```tsx title="packages/ui/src/button.tsx"
export interface ButtonProps {
  children: React.ReactNode
  onClick?: () => void
  variant?: 'primary' | 'secondary'
}

export function Button({ children, onClick, variant = 'primary' }: ButtonProps) {
  const baseStyles = {
    padding: '0.75rem 1.5rem',
    fontSize: '1rem',
    border: 'none',
    borderRadius: '0.5rem',
    cursor: 'pointer',
    fontWeight: '600',
  }

  const variantStyles = {
    primary: {
      backgroundColor: '#0070f3',
      color: 'white',
    },
    secondary: {
      backgroundColor: '#f5f5f5',
      color: '#333',
      border: '1px solid #e5e7eb',
    },
  }

  return (
    <button
      onClick={onClick}
      style={{ ...baseStyles, ...variantStyles[variant] }}
    >
      {children}
    </button>
  )
}
```

The `variant` prop lets you choose between button styles: `primary` (blue, for main actions) or `secondary` (gray, for less prominent actions). This makes the Button reusable across different contexts without hardcoding the styles.

## Add button to package exports

Update `packages/ui/package.json`:

```json title="packages/ui/package.json" {3}
{
  "exports": {
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx"
  }
}
```

Export from the index file:

```ts title="packages/ui/src/index.ts"
export { Button } from './button'
export { Card } from './card'
```

## Use button in home page

Update the home page to use the shared `Button`:

```tsx title="apps/web/app/page.tsx" {2,19}
import Link from 'next/link'
import { Button } from '@geniusgarage/ui/button'

export default function Home() {
  return (
    <main style={{ padding: '2rem', fontFamily: 'system-ui', maxWidth: '800px', margin: '0 auto' }}>
      <nav style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '2rem' }}>
        <Link href="/features" style={{ textDecoration: 'none', color: '#0070f3', fontWeight: 'bold' }}>
          Features
        </Link>
      </nav>

      <div style={{ textAlign: 'center', marginTop: '4rem' }}>
        <h1 style={{ fontSize: '3rem', marginBottom: '1rem' }}>🧠 GeniusGarage</h1>
        <p style={{ fontSize: '1.5rem', color: '#666', marginBottom: '2rem' }}>
          Manage and share your code snippets
        </p>

        <Button>Get Started</Button>

        <p style={{ color: '#666', marginTop: '3rem', fontSize: '0.875rem' }}>
          This is the starter project. You'll build out the full platform as you progress through the course.
        </p>
      </div>
    </main>
  )
}
```

Much cleaner! The inline button styles are gone.

## Run and see it work

Start the dev server:

```bash
pnpm dev
```

Open **<http://localhost:3000>** - the button looks identical but now uses the shared component.

## Test the variant prop

Let's verify the variant prop works. Change the `Button` to use the secondary variant:

```tsx title="apps/web/app/page.tsx" {1}
<Button variant="secondary">Get Started</Button>
```

Refresh the browser. The button should now be gray with a border instead of blue.

Change it back to primary (or remove the variant prop since it defaults to primary):

```tsx title="apps/web/app/page.tsx" {1}
<Button>Get Started</Button>
```

The button returns to blue. The variant system works!

## What you built

You now have two shared components in your UI package:

**packages/ui:**

- `Card` component (manually extracted in previous lesson)
- `Button` component with variant support (primary and secondary)
- Proper TypeScript types exported for both

**The pattern you've learned:**

1. Create component in `packages/ui/src/`
2. Add to `package.json` exports field
3. Export from `index.ts`
4. Import in any app with `@geniusgarage/ui/component-name`

This pattern scales - you could have 50 components following this exact structure.

## Commit

Save your work:

```bash
git add .
git commit -m "feat(ui): extract Button component with variants"
```

## Done-when

Verify your implementation:

- [ ] Button component exists at `packages/ui/src/button.tsx` with variant prop
- [ ] Button exported from `packages/ui/package.json` exports field
- [ ] Button exported from `packages/ui/src/index.ts`
- [ ] Home page uses `<Button>` from `@geniusgarage/ui/button`
- [ ] Button displays correctly on home page

## What's Next

Next lesson: **Deploy Web App** - you'll push to GitHub, deploy to Vercel, and see Turborepo's remote caching in production. Change a README → cache hit in CI. Change the UI package → selective rebuild. This is where monorepos with Turborepo really shine at scale.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
