| name | react-setup |
| description | Use when initializing a new React frontend with Vite to connect to a Django backend over HTTPS. Sets up routing, CSRF protection, Axios config, and validates the build. Not for existing React projects. |
| allowed-tools | Bash, Write, Edit, Read, Glob, Grep, TodoWrite, Task |
Overview
Sets up a production-ready React + Vite frontend configured for Django backend integration with:
- HTTPS local development (using mkcert certificates)
- CSRF token handling for Django session auth
- Axios interceptors for automatic CSRF injection
- React Router with layout structure
- Production build validation
When to use: New React projects that need Django backend connectivity
When NOT to use: Existing React projects, non-Django backends, or projects not using session auth
Prerequisites
- Django backend configured with HTTPS
- mkcert certificates in
/certs/ directory (use mkcert-https-setup skill)
- Node.js 18+ installed
Copy this checklist and track your progress:
- Initialize React + Vite Project
npm create vite@latest frontend -- --template react --yes
npm --prefix ./frontend install react-router-dom axios
-
Configure vite.config.js:
-
List cert files in certs/ to find actual filenames
-
Read existing frontend/vite.config.js (created by Vite)
-
Add imports: fs, path
-
Add server config:
- server.https: Load key/cert from
certs/ using found filenames
- server.port: 5173
- server.proxy:
/api → https://localhost:8000 (changeOrigin: true, secure: false)
- Create API Configuration
src/config/api.js:
export const API_BASE_URL = '';
export const ENDPOINTS = {
CSRF: '/api/csrf/',
};
- Create CSRF Service
src/services/csrf.js:
import { ENDPOINTS } from '../config/api';
let csrfToken = null;
export async function getCsrfToken() {
if (csrfToken) {
return csrfToken;
}
try {
const response = await fetch(ENDPOINTS.CSRF, {
credentials: 'include',
headers: {
'Accept': 'application/json',
}
});
if (!response.ok) {
throw new Error(`Failed to fetch CSRF token: ${response.status}`);
}
const cookies = document.cookie.split(';');
const csrfCookie = cookies.find(cookie => cookie.trim().startsWith('csrftoken='));
if (!csrfCookie) {
throw new Error('CSRF token not found in cookies');
}
csrfToken = csrfCookie.split('=')[1].trim();
return csrfToken;
} catch (error) {
console.error('CSRF token fetch error:', error);
throw error;
}
}
export function clearCsrfToken() {
csrfToken = null;
}
- Create Axios Configuration
src/services/api.js:
import axios from 'axios';
import { API_BASE_URL } from '../config/api';
import { getCsrfToken } from './csrf';
const axiosInstance = axios.create({
baseURL: API_BASE_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
axiosInstance.interceptors.request.use(
async (config) => {
if (['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase())) {
const csrfToken = await getCsrfToken();
config.headers['X-CSRFToken'] = csrfToken;
}
return config;
}
);
axiosInstance.interceptors.response.use(
response => response,
error => {
console.error('API request failed:', error);
return Promise.reject(error);
}
);
export default axiosInstance;
- Create Navbar and Footer Components
Create src/components/Navbar.jsx:
export default function Navbar() {
return (
<nav>
<h1>My App</h1>
</nav>
);
}
Create src/components/Footer.jsx:
export default function Footer() {
return (
<footer>
<p>© {new Date().getFullYear()} My App. All rights reserved.</p>
</footer>
);
}
- Create
src/layouts/Root.jsx:
import { Outlet } from 'react-router-dom';
import Navbar from '../components/Navbar';
import Footer from '../components/Footer';
export default function Root() {
return (
<div>
<Navbar />
<main>
<Outlet />
</main>
<Footer />
</div>
);
}
- Create Page Components
Create src/components/BackendStatus.jsx:
- Import axios from
../services/api, useState, useEffect
- Initialize state:
status = "checking...", error = null
- useEffect hook (runs on mount):
- First:
axios.get('/api/csrf/') to initialize cookies
- Second:
axios.get('/api/health/') using same axios instance
- Success path: update status with backend message
- Error path: set error state, display "Backend unreachable"
- Component is presentational only (no routing/layout logic)
Create src/pages/Home.jsx:
- Render
<BackendStatus />
- Add heading + text explaining the backend connectivity check
-
Create src/router/AppRoutes.jsx:
-
Export createAppRouter() function
-
Returns route config array: Root layout at "/" with Home page as nested "/" child route
-
Imports: Root from ../layouts/Root, Home from ../pages/Home
-
Create src/router/index.jsx:
-
Export default AppRouter component
-
Create browser router from createAppRouter() config
-
Return <RouterProvider router={router} />
-
Imports: createBrowserRouter, RouterProvider from react-router-dom, createAppRouter from ./AppRoutes
- Update
src/App.jsx:
- Remove: All useState, demo imports (reactLogo, viteLogo, App.css), counter UI
- Add imports:
Router from ./router
- Render:
<Router />
- Verify Setup
Run npm --prefix ./frontend run build to validate imports, dependencies, and syntax
Common issues:
- Missing certs: Check
/certs/ directory exists with .pem files
- Module not found: Verify all imports use correct paths
- CSRF errors: Ensure Django backend is running at
https://localhost:8000