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
# 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:
// components/ConferenceCard.tsx — RSC (no directive)
import { Conference } from '@/types/conference'
interface Props {
conference: Conference
}
export function ConferenceCard({ conference }: Props) {
return (
<div>
<h2>{conference.title}</h2>
<p>{conference.description}</p>
</div>
)
}
Validation per RSC file:
# 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:
'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 (
<form onSubmit={handleSubmit}>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
<button type="submit">Create</button>
</form>
)
}
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,useSearchParamscome fromnext/navigation
B4 — Extract Context Providers
For each provider in the Provider Wrappers list:
// 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<ConferenceContextType | null>(null)
// 2. Export the provider as a wrapper component
export function ConferenceProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
return (
<ConferenceContext.Provider value={{ state, setState }}>
{children}
</ConferenceContext.Provider>
)
}
// 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):
// app/layout.tsx — RSC, imports client provider
import { ConferenceProvider } from './providers/conference-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body>
<ConferenceProvider>
{children}
</ConferenceProvider>
</body>
</html>
)
}
B5 — Verify
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 ✅.