---
name: react-to-nextjs-components
description: >
Implements Phase B of the React-to-Next.js migration plan: classifies and
migrates React components to Server Components (RSC) or Client Components
(RCC), extracts Context providers into client wrappers, and adds 'use client'
directives where required. Use this skill whenever the user asks to migrate
components, convert JSX to RSC, add use client directives, or execute Phase B
of a MIGRATION_PLAN.md. Requires Phase A (routing) to be complete first.
metadata:
author: app-dev-exp
version: "1.0"
---
# react-to-nextjs-components
Implements **Phase B** of the migration plan: component classification and
migration to RSC/RCC. Writes files directly into `ms-conference-webapp/`.
---
## Preconditions
```bash
# Always read paths first
cat agents/shared/context/monorepo-paths.md
WEBAPP_ROOT="conference-manager/ms-conference-webapp"
WEBAPP_LEGACY="conference-manager/ms-conference-webapp/legacy"
WEBAPP_APP="conference-manager/ms-conference-webapp/src/app"
FRONTEND_PLANS="conference-manager/ms-conference-webapp/plans"
# Phase A must be complete
grep "Phase A" $FRONTEND_PLANS/MIGRATION_PLAN.md | grep "✅"
# No TypeScript errors from Phase A
cd $WEBAPP_ROOT && npx tsc --noEmit
```
If Phase A is not marked ✅ → stop and report.
---
## Phase B execution steps
### B1 — Read the plan
Extract from `MIGRATION_PLAN.md` Phase B section:
- Server Components list (RSC)
- Client Components list (RCC) with reasons
- Provider Wrappers needed
### B2 — Migrate Server Components (RSC)
For each component in the RSC list — no directive needed, just ensure no
client-only APIs are present:
```typescript
// components/ConferenceCard.tsx — RSC (no directive)
import { Conference } from '@/types/conference'
interface Props {
conference: Conference
}
export function ConferenceCard({ conference }: Props) {
return (
{conference.title}
{conference.description}
)
}
```
**Validation per RSC file:**
```bash
# Confirm no client-only APIs leaked in
grep -n "useState\|useEffect\|useRef\|onClick\|onChange\|window\.\|document\." \
path/to/component.tsx
# Must return 0 matches
```
### B3 — Migrate Client Components (RCC)
For each component in the RCC list — add `'use client'` as the very first line:
```typescript
'use client'
// components/ConferenceForm.tsx — RCC (has interactivity)
import { useState } from 'react'
import { useRouter } from 'next/navigation'
export function ConferenceForm() {
const [title, setTitle] = useState('')
const router = useRouter()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// submit logic
router.push('/conferences')
}
return (
)
}
```
**Rules for RCC:**
- `'use client'` must be the first line — before imports
- All hooks remain unchanged (`useState`, `useEffect`, etc.)
- Event handlers remain unchanged
- `useRouter`, `usePathname`, `useSearchParams` come from `next/navigation`
### B4 — Extract Context Providers
For each provider in the Provider Wrappers list:
```typescript
// app/providers/{name}-provider.tsx — always 'use client'
'use client'
import { createContext, useContext, useState } from 'react'
// 1. Move context definition here from original file
const ConferenceContext = createContext(null)
// 2. Export the provider as a wrapper component
export function ConferenceProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
return (
{children}
)
}
// 3. Export the hook for consumption in RCC
export function useConference() {
const ctx = useContext(ConferenceContext)
if (!ctx) throw new Error('useConference must be used within ConferenceProvider')
return ctx
}
```
Then import the provider in `app/layout.tsx` (which stays as RSC):
```typescript
// app/layout.tsx — RSC, imports client provider
import { ConferenceProvider } from './providers/conference-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
```
### B5 — Verify
```bash
cd conference-manager/ms-conference-webapp
# TypeScript check
npx tsc --noEmit
# Verify no RSC has client-only APIs without 'use client'
grep -rL "use client" app/components/ --include="*.tsx" | \
xargs grep -l "useState\|useEffect\|onClick\|onChange" 2>/dev/null
# Must return empty — any match is a missing 'use client'
# Verify 'use client' is first line where present
grep -rn "use client" app/ --include="*.tsx" | \
grep -v "^.*:1:'use client'" | grep -v "^.*:1:\"use client\""
# Must return empty — 'use client' not on line 1 is an error
```
---
## Completion report
```
✅ Phase B Complete — Component Migration
Server Components (RSC): {count} files
Client Components (RCC): {count} files
Provider wrappers created: {count} files
Files created/modified:
- {list}
TypeScript: ✅ 0 errors
Ready for Phase C: react-to-nextjs-data-fetching
```
Update `MIGRATION_PLAN.md` marking Phase B items with ✅.