# Frontend Next Add Field

> Add a new field to an existing Next.js storefront feature: types, Zod schema, form input, API payload, and display components. Use when extending an existing feature with a new property from the backend.

- Skill: `xmuhameed/frontend-next-add-field` (Agent Skill)
- Install (CLI): `npx skillmds@latest add xmuhameed/frontend-next-add-field`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xmuhameed/frontend-next-add-field/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: xmuhameed (https://skillmd.com/u/xmuhameed)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/xmuhameed/frontend-next-add-field

---


# Add Field to Existing Next.js Feature

**Not** for new features or pages — use `@frontend-next/create-feature` or `@frontend-next/create-page`.

**Reference:** `src/features/{name}/` or related `components/`, `services/`, `types/`.

## Checklist

```
[ ] 1. Types — entity interface + form/payload types
[ ] 2. Zod schema — validation in utils/ or feature folder
[ ] 3. Form — FormField or controlled input + defaultValues
[ ] 4. API service — include field in request body/params
[ ] 5. Display — product card, detail page, checkout summary, etc.
[ ] 6. Store (if persisted) — Zustand slice field + actions
[ ] 7. i18n — labels in messages/en.json + ar.json if user-facing
[ ] 8. Server fetch — update RSC fetch types/select if SSR page
```

## Step 1 — Types

```typescript
// features/product/types/product.types.ts
export interface Product {
  id: string;
  name_en: string;
  name_ar: string;
  sku?: string | null;
  isFeatured: boolean;
}

export interface UpdateProfilePayload {
  phone?: string;
  companyName?: string;  // new
}
```

Keep types aligned with backend DTOs and API responses.

## Step 2 — Zod schema

See `@frontend-next/forms`.

```typescript
// utils/validation.ts or features/checkout/utils/validation.ts
export const profileSchema = z.object({
  phone: z.string().min(1),
  companyName: z.string().optional(),
});
```

| Backend type | Zod |
|--------------|-----|
| optional string | `z.string().optional()` |
| email | `z.string().email()` |
| phone | custom refine with `libphonenumber-js` |
| conditional required | `.refine()` on object |

## Step 3 — Form input

```tsx
<FormField control={form.control} name="companyName" render={({ field }) => (
  <FormItem>
    <FormLabel>{t('companyName')}</FormLabel>
    <FormControl><Input {...field} value={field.value ?? ''} /></FormControl>
    <FormMessage />
  </FormItem>
)} />
```

**Auth forms** may use controlled `useState` — add the field there instead of RHF.

**File inputs** — separate `useState<File | null>`, not in RHF.

**Prefill from auth/API:**

```typescript
useEffect(() => {
  if (user) form.reset({ ...form.getValues(), companyName: user.companyName ?? '' });
}, [user]);
```

## Step 4 — API layer

See `@frontend-next/api-layer`.

**Client service** (mutations):

```typescript
export const updateProfile = (payload: UpdateProfilePayload) =>
  api.patch('/user/update-profile', payload);
```

**Server fetch** (RSC pages) — extend return type and ensure backend includes the field:

```typescript
// lib/api/products.ts
export async function getProduct(slug: string): Promise<Product> { ... }
```

## Step 5 — Display components

Update every UI surface that shows the entity:

| Surface | Action |
|---------|--------|
| Detail page | render new field |
| Card / list item | show if relevant (badge, subtitle) |
| Checkout summary | include in line items or address block |
| Order confirmation | display persisted value |

Use locale-aware field for bilingual data:

```typescript
const name = locale.startsWith('ar') ? product.name_ar : product.name_en;
```

## Step 6 — Zustand store (if applicable)

See `@frontend-next/state-management`.

```typescript
interface CartItem {
  productId: string;
  quantity: number;
  giftMessage?: string;  // new
}
```

Update actions that create/update the item shape.

## Step 7 — i18n

See `@frontend-next/i18n`.

```json
// messages/en.json
{ "companyName": "Company name" }

// messages/ar.json
{ "companyName": "اسم الشركة" }
```

Only add translations for user-visible labels — not internal admin fields on storefront unless shown.

## Step 8 — SEO / metadata (if public field)

If the new field affects page content (e.g. `metaDescription`), update `generateMetadata` — see `@frontend-next/seo`.

## Field-type quick reference

| Field | Types | Form | Display |
|-------|-------|------|---------|
| String | `string?` | `Input`, `Textarea` | text |
| Boolean | `boolean` | `Checkbox` | conditional UI |
| Enum | union | `Select`, `RadioGroup` | mapped label |
| Price/money | `number` | formatted input | `formatPrice()` |
| Date | `string` ISO | date picker | formatted date |
| Image URL | `string` | file state | `next/image` + `getMediaUrl` |
| Bilingual | `name_en`/`name_ar` | both inputs (admin) or locale pick (display) | locale switch |

## Verification

```
[ ] Types match API response
[ ] Form validates and submits new field
[ ] Client mutation payload correct
[ ] Server-rendered pages show new field (if applicable)
[ ] Translations added for user-facing labels
[ ] No stale React Query cache — invalidate relevant keys
```

