# Add Feature

> Scaffold a new frontend feature module with page, components, API service, and hooks. Use whenever the user wants to add a new page, dashboard section, UI module, or frontend feature to the React app. Use when this capability is needed.

- Skill: `tomevault-io/add-feature-4` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/add-feature-4`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/add-feature-4/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/add-feature-4

---


# Add Frontend Feature

Scaffold a new feature module following the project's React + TypeScript conventions.

## Arguments

- `{feature}` — Feature name in kebab-case (e.g., `alerts`, `rollouts`)

## Structure

Creates the following structure under `web/src/features/{feature}/`:

```
{feature}/
├── pages/
│   └── {feature}-page.tsx
└── components/
    └── {feature}-overview.tsx
```

And adds an API service at `web/src/api/services/{feature}.api.ts`.

## 1. API Service (`web/src/api/services/{feature}.api.ts`)

```typescript
/**
 * {Feature} API client
 */

import { apiRequest } from '../client'
import { appendTenantId } from './tenant'
import type { PaginatedResponse } from '../types'

// Define types inline or in ../types/index.ts
export interface {Entity} {
  id: string
  // fields
}

const BASE_PATH = '/api/{feature}'

export const {feature}Api = {
  /**
   * Get paginated list
   */
  async getAll(page = 1, pageSize = 20): Promise<PaginatedResponse<{Entity}>> {
    const params = new URLSearchParams()
    appendTenantId(params)
    params.set('pageNumber', page.toString())
    params.set('pageSize', pageSize.toString())

    return apiRequest({ url: BASE_PATH, params })
  },

  /**
   * Get by ID
   */
  async getById(id: string): Promise<{Entity}> {
    const params = new URLSearchParams()
    appendTenantId(params)
    return apiRequest({ url: `${BASE_PATH}/${id}`, params })
  },
}
```

## 2. Page (`web/src/features/{feature}/pages/{feature}-page.tsx`)

```tsx
import { {Feature}Overview } from '../components/{feature}-overview'

export function {Feature}Page() {
  return (
    <div className="container mx-auto py-6">
      <{Feature}Overview />
    </div>
  )
}
```

## 3. Overview Component (`web/src/features/{feature}/components/{feature}-overview.tsx`)

```tsx
import { useQuery } from '@tanstack/react-query'
import { {feature}Api } from '@/api/services/{feature}.api'

export function {Feature}Overview() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['{feature}'],
    queryFn: () => {feature}Api.getAll(),
    staleTime: 30_000,
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error loading {feature}</div>

  return (
    <div>
      <h1 className="text-2xl font-bold mb-4">{Feature}</h1>
      {/* Render data */}
    </div>
  )
}
```

## 4. Add Route

Find the router configuration file and add the route automatically:

```bash
# Find the router file
grep -rl "createBrowserRouter\|RouteObject\|path:" web/src/routes/ web/src/App.tsx 2>/dev/null | head -1
```

Read the router file, then add the import and route entry:

```tsx
import { {Feature}Page } from '@/features/{feature}/pages/{feature}-page'

// Add to the routes array alongside existing routes:
{ path: '/{feature}', element: <{Feature}Page /> }
```

If the router structure is unclear or uses a pattern you don't recognize, show the user what to add and where instead of guessing.

## Checklist

- [ ] API service uses `apiRequest` and `appendTenantId`
- [ ] Types defined as interfaces, no `any`
- [ ] TanStack Query for server state
- [ ] Functional components only
- [ ] shadcn/ui components with Tailwind CSS
- [ ] Route added to router config

## Guidelines

- Follow existing features (e.g., `devices/`, `bundles/`) as reference
- Keep components small and focused
- Use barrel exports where the project already does
- If using unfamiliar TanStack Query patterns (infinite queries, optimistic updates, prefetching), use context7 to look up the current API

## Output

After scaffolding, report:
```
## Scaffolded: {Feature} frontend feature

Files created:
- `web/src/api/services/{feature}.api.ts`
- `web/src/features/{feature}/pages/{feature}-page.tsx`
- `web/src/features/{feature}/components/{feature}-overview.tsx`
- Route added to `{router-file}`

Next: Run `cd web && npm run dev` to preview, or `/verify-feature /{feature}` to verify.
```

## Error Handling

- **Router file not found:** Show the route snippet and ask the user where to add it.
- **API client (`apiRequest`) doesn't exist:** Check `web/src/api/client.ts` — if the pattern differs, adapt to the existing API client setup.
- **Feature directory already exists:** Warn the user and ask whether to overwrite or extend.

## Related Skills

- `/add-query` to create the backend API endpoint this feature calls
- `/add-command` to create mutation endpoints

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/signalbeam-io) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

