Learn the core patterns for building extensions: how to bootstrap your extension, declare UI surfaces, manage cross-surface state, and handle loading and error states gracefully.

## Bootstrap — Entry Point

Set up your extension entry point in `src/index.tsx`. The `createExtension` factory
bootstraps the sandboxed runtime and registers all surfaces with the framework.

```tsx
import { createExtension } from '@stackable-labs/sdk-extension-react'
import { Header } from './surfaces/Header'
import { Content } from './surfaces/Content'
import { Footer } from './surfaces/Footer'

const Extension = () => (
  <>
    <Header />
    <Content />
    <Footer />
  </>
)

// NOTE: extensionId is optional — used when connected to a registered extension
createExtension(() => <Extension />, { extensionId: 'my-extension' })
```

## Surfaces — Declaring UI Slots

Each surface renders into a specific layout slot in the embedding application.
The `id` prop must match a target declared in your `manifest.json`.

### Header

```tsx
import { Surface, ui } from '@stackable-labs/sdk-extension-react'

export function Header() {
  return (
    <Surface id="slot.header">
      <ui.Text className="text-sm font-medium">Header content</ui.Text>
    </Surface>
  )
}
```

### Content

```tsx
import { Surface, ui } from '@stackable-labs/sdk-extension-react'

export function Content() {
  return (
    <Surface id="slot.content">
      <ui.Card>
        <ui.CardContent>
          <ui.Text>Extension content</ui.Text>
        </ui.CardContent>
      </ui.Card>
    </Surface>
  )
}
```

### Footer

```tsx
import { Surface, ui } from '@stackable-labs/sdk-extension-react'

export function Footer() {
  return (
    <Surface id="slot.footer">
      <ui.Text className="text-xs">Powered by My Extension</ui.Text>
    </Surface>
  )
}
```

### Footer Links

```tsx
import { Surface, ui } from '@stackable-labs/sdk-extension-react'

export function FooterLinks() {
  return (
    <Surface id="slot.footer-links">
      <ui.FooterLink href="https://example.com">My Extension</ui.FooterLink>
    </Surface>
  )
}
```

## Store — Cross-Surface State & Navigation

Extensions use `createStore` for shared state across surfaces. The store replaces
traditional URL routing — there are no URLs inside the sandbox. Use discriminated
unions for view state to get exhaustive type checking.

```tsx
import { createStore, useStore, Surface, ui } from '@stackable-labs/sdk-extension-react'

// store.ts
type ViewState = { type: 'list' } | { type: 'detail'; id: string }

interface AppState {
  viewState: ViewState
}

export const appStore = createStore<AppState>({
  viewState: { type: 'list' },
})

// Content.tsx
export function Content(): React.ReactElement {
  const viewState = useStore(appStore, (s) => s.viewState)

  const goToDetail = (id: string) => appStore.set({ viewState: { type: 'detail', id } })
  const goBack = () => appStore.set({ viewState: { type: 'list' } })

  return (
    <Surface id="slot.content">
      {(viewState.type === 'list') && (
        <ui.Button onClick={() => goToDetail('abc123')}>View Detail</ui.Button>
      )}
      {(viewState.type === 'detail') && (
        <ui.Stack direction="column" gap="2">
          <ui.Text>Viewing: {viewState.id}</ui.Text>
          <ui.Button onClick={goBack}>Back</ui.Button>
        </ui.Stack>
      )}
    </Surface>
  )
}
```

## Loading States & Guards

Handle loading states and missing context gracefully. Always show a skeleton
while context loads, and guard against missing data before rendering.

```tsx
import { useContextData, Surface, ui } from '@stackable-labs/sdk-extension-react'

export function Content(): React.ReactElement {
  const { loading, customerId } = useContextData()

  if (loading) {
    return (
      <Surface id="slot.content">
        <ui.Skeleton />
      </Surface>
    )
  }

  if (!customerId) {
    return (
      <Surface id="slot.content">
        <ui.Stack direction="column" gap="1" className="p-4 items-center">
          <ui.Icon name="user" size="sm" />
          <ui.Text className="text-xs text-muted-foreground">No customer selected</ui.Text>
        </ui.Stack>
      </Surface>
    )
  }

  return (
    <Surface id="slot.content">
      <ui.Text className="text-sm">Customer: {customerId}</ui.Text>
    </Surface>
  )
}
```

## Surface Context

Read host-pushed context for a specific surface using the lower-level
`useSurfaceContext` hook.

```tsx
import { useSurfaceContext, Surface, ui } from '@stackable-labs/sdk-extension-react'
import type { ContextData } from '@stackable-labs/sdk-extension-contracts'

export function Header(): React.ReactElement {
  // Lower-level hook — reads host-pushed context for this specific surface
  const context = useSurfaceContext() as ContextData

  return (
    <Surface id="slot.header">
      <ui.Text className="text-xs">{context.customerId ?? 'No customer'}</ui.Text>
    </Surface>
  )
}
```
