React Development
Role in This Project
React is used only for interactive islands — calculator forms, dynamic UI, and anything that needs client-side state. All static content is rendered by Astro.
Component Pattern
tsx
interface TileCalculatorProps {
baseUrl?: string;
}
export default function TileCalculator({ baseUrl = '' }: TileCalculatorProps) {
// component logic
return (/* JSX */);
}
Do NOT use FC type. Use regular function declarations with typed props.
Rules
- •Functional components only. No class components.
- •Export as default from calculator component files (required for Astro island imports).
- •No business logic in components. Import calculator functions from
src/calculators/. Components handle state and UI only. - •Use controlled form inputs with
useStatefor calculator forms.
State Management
- •Local state:
useStatefor component-scoped state (form inputs, results). - •Cross-island state: Use Nanostores if multiple islands need shared state. Nanostores works with both Astro and React.
- •Do NOT use Zustand, Redux, or Context — Nanostores is the standard for Astro multi-framework islands.
Calculator Component Structure
tsx
import { useState } from 'react';
import { calculateTiles } from '../../calculators/tiles';
import type { TileInput, TileResult } from '../../calculators/tiles';
export default function TileCalculator() {
const [input, setInput] = useState<TileInput>(defaultInput);
const [result, setResult] = useState<TileResult | null>(null);
const handleCalculate = () => {
setResult(calculateTiles(input));
};
return (
<div>
{/* Input form */}
{/* Calculate button */}
{/* Results display (conditional on result !== null) */}
</div>
);
}
Performance
- •Use
useMemofor expensive derived calculations. - •Use
useCallbackonly when passing callbacks to memoised children. - •Do NOT premature-optimise — profile first.