Author: Noah Peden
Creation Date: May 2, 2025
Last Updated: May 2, 2025
This document outlines the complete workflow for migrating the frontend from Django templates and vanilla JavaScript to a modern, decoupled frontend using Next.js 15, React 19, and TypeScript. The backend APIs will remain within the existing Django codebase.
- Framework: Next.js 15 (App Router)
- Library: React 19
- Language: TypeScript (ESM-only)
- Styling: TailwindCSS with DaisyUI
- State Management: React Query (for server state) + Zustand (for client state)
- Testing: Jest, React Testing Library, Cypress, MSW (Mock Service Worker)
- Component Documentation: Storybook
- Performance Monitoring: Vercel Analytics
- Deployment: Vercel Edge Runtime
Core Principle: Clearly define frontend/backend separation, document explicit API contracts, and implement modular architecture from the start.
- Record ideation sessions, product demos, and initial stakeholder meetings (with product managers, salespeople, etc.) using Loom.
- Utilize Loom's AI transcription capabilities to summarize key points, action items, and ideas.
- Export and maintain these transcripts in markdown (
loom-ideas.md) for reference during PRD creation.
-
Using Loom transcripts (
loom-ideas.md), prompt Gemini 2.5 Pro Thinking to create a structured, detailed Frontend Rewrite PRD (frontend-rewrite-prd.md) that clearly covers:- User workflows and interactions
- Required frontend functionalities
- API integration points with existing Django backend
- Necessary UI/UX enhancements
Review carefully with stakeholders, refining until approval.
- From your approved PRD, begin rapid prototyping and design iteration using v0.
- Quickly iterate and validate UI/UX concepts with stakeholders and users.
- Export finalized prototypes as initial scaffolding code for Next.js 15.
- Create a Storybook instance for component documentation and isolated testing.
-
Confirm the finalized frontend tech stack for the project using Gemini 2.5 Pro Thinking, documenting explicitly in
frontend-tech-stack.md. -
Configure
.cursor/ruleswithin Cursor to emphasize:- Modular component-driven frontend architecture.
- ESM-only module system
- Critical "Always" rules:
# IMPORTANT:
# Always review memory-bank/@architecture.md before writing code.
# Always reference memory-bank/@frontend-rewrite-prd.md before writing any code.
# Always use ESM imports/exports (no CommonJS).
# Always prefer server components where possible, using client components only when necessary.
# Always validate data fetching patterns against Next.js 15 best practices.
# After significant features or milestones, update memory-bank/@architecture.md clearly defining file/component roles and responsibilities.
-
For each product epic defined in the PRD, create a dedicated Software Design Document (SDD):
- Clearly document architecture, technical constraints, component relationships, and API interactions.
- Include explicit Implementation Technical Details (ITDs) to provide technical clarity and developer instructions.
- Define data fetching strategy (server vs. client components)
- Document component boundaries and prop interfaces
-
Within each epic's directory, maintain markdown-based task tracker documents (
tasks.md):- Clearly define individual implementation tasks and acceptance criteria.
- Regularly update these markdown files for project transparency.
-
Provide Gemini 2.5 Pro Thinking with:
- Loom transcripts (
loom-ideas.md) - PRD (
frontend-rewrite-prd.md) - SDDs and ITDs (
epic-sdd-itd.md) - Tech stack documentation (
frontend-tech-stack.md) - Cursor rules (
.cursor/rules)
- Loom transcripts (
-
Request a detailed Frontend Implementation Plan (
frontend-implementation-plan.md) with clear incremental steps and explicit tests.
-
Within your Cursor project, create a dedicated
memory-bankfolder containing:loom-ideas.mdfrontend-rewrite-prd.md- Epic-specific SDD/ITD documents (
epic-sdd-itd.md) frontend-tech-stack.mdfrontend-implementation-plan.mddata-fetching-strategy.md(Server Components vs. Client Components)progress.md(initially empty)architecture.md(initially empty)
(Cursor will automatically generate the .cursor/rules file.)
-
Using Claude Sonnet 3.7 Thinking in Cursor:
- Prompt: Review
/memory-bank. Isfrontend-implementation-plan.mdclear? Specify any needed clarifications. - Update the implementation plan based on Claude's queries.
- Prompt: Review
-
Prompt:
Read
/memory-bank. Proceed with Step 1 fromfrontend-implementation-plan.md. Wait for my test validation before continuing further. Upon successful validation, updateprogress.mdclearly documenting the work done, and record the purpose of created files/components withinarchitecture.md.
-
Initialize Next.js 15 with the App Router
npx create-next-app@latest frontend-project --typescript --eslint --app --tailwind --import-alias "@/*" -
ESM Configuration Set
"type": "module"in package.json and ensure all imports use ESM syntax. -
Install Core Dependencies
npm install @tanstack/react-query zustand daisyui@latest npm install -D @types/node @types/react @types/react-dom typescript eslint eslint-config-next jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom msw storybook
-
Configure DaisyUI
// tailwind.config.js module.exports = { content: [ './app/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}', ], theme: { extend: {}, }, plugins: [require('daisyui')], daisyui: { themes: ['light', 'dark'], }, }
-
Setup Core Folder Structure
/app /api // API route handlers /(authenticated) // Route group for authenticated pages /(marketing) // Route group for marketing pages /components /ui // Base UI components /features // Feature-specific components /hooks // Custom React hooks /lib // Utility functions /styles // Global styles and theme configuration /memory-bank // Documentation
Implement a hybrid strategy leveraging Next.js 15's Server Components and Client Components:
-
Server Components (Default)
- Use for data-fetching directly from the backend
- Implement with async/await pattern
- Utilize React Server Components for initial page loads
// app/users/page.tsx (Server Component) export default async function UsersPage() { const users = await fetch('https://api.example.com/users').then(res => res.json()); return ( <div> <h1 className="text-2xl font-bold">Users</h1> <UserList initialData={users} /> </div> ); }
-
Client Components (When Needed)
- Use for interactive elements requiring React hooks
- Implement React Query for client-side data fetching, caching, and mutations
- Utilize Zustand for UI state management
// components/features/UserList.tsx (Client Component) 'use client'; import { useQuery } from '@tanstack/react-query'; export default function UserList({ initialData }) { const { data: users } = useQuery({ queryKey: ['users'], queryFn: () => fetch('/api/users').then(res => res.json()), initialData, }); return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
-
API Routes for Client-Side Fetching Implement API routes to proxy requests to the Django backend:
// app/api/users/route.ts import { NextResponse } from 'next/server'; export async function GET() { const response = await fetch('https://backend.example.com/api/users/', { headers: { // Add authentication headers if needed }, }); const data = await response.json(); return NextResponse.json(data); }
-
Setup MSW
npm install -D msw
-
Define API Mocks
// mocks/handlers.ts import { rest } from 'msw'; export const handlers = [ rest.get('https://api.example.com/users', (req, res, ctx) => { return res( ctx.status(200), ctx.json([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ]) ); }), ];
-
Integrate with Tests
// __tests__/UserList.test.tsx import { render, screen } from '@testing-library/react'; import { setupServer } from 'msw/node'; import { handlers } from '../mocks/handlers'; const server = setupServer(...handlers); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); test('renders users', async () => { render(<UserList />); expect(await screen.findByText('Alice')).toBeInTheDocument(); expect(await screen.findByText('Bob')).toBeInTheDocument(); });
-
Follow structured, iterative implementation:
- Incrementally validate with clear tests at every step.
- Commit frequently to Git, ensuring easy rollback.
- Prompt Claude explicitly to reference updated
progress.mdbefore continuing to the next step.
-
Configure Vercel Analytics
npm install @vercel/analytics
// app/layout.tsx import { Analytics } from '@vercel/analytics/react'; export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Analytics /> </body> </html> ); }
-
Implement Performance Budget
- Set clear performance thresholds in
performance-budget.md - Track Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS)
- Set clear performance thresholds in
Configure pages that benefit from edge computing:
// app/api/real-time-data/route.ts
export const runtime = 'edge';
export async function GET() {
// This will run in Vercel's Edge Runtime for lower latency
const data = await fetchDataFromSource();
return Response.json(data);
}Utilize Next.js built-in Image component with proper settings:
import Image from 'next/image';
export default function OptimizedImage() {
return (
<Image
src="/path/to/image.jpg"
alt="Description"
width={800}
height={600}
priority={true} // For LCP images
placeholder="blur" // For better UX
/>
);
}Implement a strict CSP in next.config.mjs:
// next.config.mjs
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://analytics.vercel.app;
connect-src 'self' https://api.example.com https://analytics.vercel.app;
img-src 'self' blob: data: https://assets.example.com;
style-src 'self' 'unsafe-inline';
frame-src 'none';
`.replace(/\s{2,}/g, ' ').trim(),
},
],
},
];
},
};
export default nextConfig;Implement secure authentication with Next.js middleware:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
// Protect authenticated routes
if (request.nextUrl.pathname.startsWith('/dashboard') && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};Post-core frontend implementation:
-
Create dedicated
feature-implementation.mdfiles for each incremental feature:- Clearly structured, test-driven implementation steps.
- Stakeholder validation based on prototypes from v0.
- Include Storybook documentation for each component
/features
/user-management
implementation.md
tasks.md
storybook.mdx
/analytics-dashboard
implementation.md
tasks.md
storybook.mdx
-
When encountering problematic code:
- Immediately revert using Cursor's restore functionality.
- Clearly specify corrections in revised prompts.
-
Debugging frontend issues:
- Use browser console errors, visual screenshots, or tools like BrowserTools for efficient debugging.
- Leverage React DevTools and Next.js debugging tools
-
If severely blocked:
- Rollback to a previous stable state (
git reset). - Utilize comprehensive debugging support via RepoPrompt or uithub in conjunction with Gemini 2.5 Pro.
- Rollback to a previous stable state (
- Small Frontend Edits: Claude Sonnet 3.7 or GPT-4.1 (non thinking versions)
- Marketing/Copywriting: GPT-4.5
- Visual/UI Design: ChatGPT-4o, Figma Tokens, Style Dictionary
- Interactive Prototyping: v0
- Prompting Best Practices: Clearly instruct the AI to carefully review relevant documentation and maintain precise, structured iterative progress.
Q: Should backend API modifications happen concurrently? A: Initially no. Keep backend stable and separately document any required backend updates, explicitly coordinating when necessary.
Q: Can existing vanilla JS logic be reused directly? A: Reference logic, but rewrite clearly in TypeScript and React to leverage modern best practices and maintainability.
Q: Is incremental deployment advisable? A: Yes, incrementally deploying helps ensure frontend stability, clear validation, and reliable rollback capability.
Q: Why use DaisyUI with Tailwind instead of Stylex? A: DaisyUI provides pre-built accessible components while maintaining Tailwind's utility-first approach. This accelerates development while ensuring consistent design patterns across the application.
Q: What are the benefits of the Edge Runtime? A: Edge Runtime reduces latency by running code closer to users, provides better global performance, and scales automatically based on demand.