This document outlines the improvement and modernization plan for the Search Algorithm Visualizer project.
A React + TypeScript pathfinding algorithm visualizer with interactive grid-based visualization.
| Aspect | Current | Target |
|---|---|---|
| Build Tool | Create React App (deprecated) | Vite |
| TypeScript | 4.9.5, strict: false | 5.5+, strict: true |
| State Management | Context API | Zustand or optimized Context |
| Styling | Plain CSS | Tailwind CSS or CSS Modules |
| Testing | Minimal | Comprehensive with Vitest + Playwright |
CRA is deprecated and unmaintained. Vite provides 10-100x faster builds.
Tasks:
- Install Vite with React and TypeScript plugins
- Create
vite.config.tswith proper configuration - Move
index.htmlto project root (Vite requirement) - Update
package.jsonscripts to use Vite commands - Remove
react-scriptsand CRA-related dependencies - Update Dockerfile to build with Vite (
/distinstead of/build)
Enable strict mode for better type safety.
Update tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}Upgrade these packages:
typescript: 4.9.5 → 5.5+@types/react: latest@types/react-dom: latesteslint: 8.x → 9.x (flat config)@typescript-eslint/*: 5.x → 8.x
Add new packages:
vite,@vitejs/plugin-reactvitest(testing)zustand(state management)
After enabling strict: true, fix all type errors:
- Replace
anytypes with proper interfaces - Add null checks for array access
- Ensure all functions have explicit return types
- Fix optional property access patterns
Example pattern to fix in App.tsx:
// Before
let algorithmResult: any = null;
// After
let algorithmResult: AlgorithmResult | null = null;Split App.tsx (457 lines) into smaller, focused components:
src/components/
├── App.tsx # Orchestration only (~50 lines)
├── Toolbar/
│ ├── index.tsx
│ ├── AlgorithmSelector.tsx
│ ├── SpeedSelector.tsx
│ ├── MazeSelector.tsx
│ └── ActionButtons.tsx
├── Visualization/
│ ├── Grid.tsx
│ ├── GridRow.tsx
│ └── Cell.tsx
└── Stats/
└── AlgorithmStats.tsx
Create reusable hooks from App.tsx logic:
src/hooks/
├── useAlgorithmExecution.ts # Algorithm running logic
├── useMazeGeneration.ts # Maze generation logic
├── useVisualization.ts # Animation and visualization
├── useGridNavigation.ts # Finding start/end nodes
└── useStats.ts # Statistics management
Consolidate types in dedicated files:
src/types/
├── algorithm.types.ts # AlgorithmResult, AlgorithmType, etc.
├── grid.types.ts # CellState, GridState, etc.
└── animation.types.ts # AnimationSpeed, AnimationState, etc.
Current issue: Updating one cell re-renders entire grid.
Solution: Direct DOM manipulation for animations
const animateCell = (row: number, col: number, type: 'visited' | 'path') => {
const cell = document.getElementById(`cell-${row}-${col}`);
cell?.classList.add(`cell-${type}`);
};Keep React state for structural changes (walls, start/end), use DOM for animations.
Replace Context API with Zustand for better performance:
// src/store/gridStore.ts
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface GridStore {
grid: CellState[][];
updateCell: (row: number, col: number, state: Partial<CellState>) => void;
resetGrid: () => void;
clearAnimations: () => void;
}
export const useGridStore = create<GridStore>()(
immer(set => ({
// implementation
}))
);Move heavy computation off the main thread:
// src/workers/algorithm.worker.ts
self.onmessage = (e: MessageEvent<AlgorithmInput>) => {
const { grid, start, end, algorithm } = e.data;
const result = executeAlgorithm(algorithm, grid, start, end);
self.postMessage(result);
};For grids larger than 50x50, add @tanstack/react-virtual:
- Only render visible cells
- Smooth scrolling support
- Significant memory savings
import { lazy, Suspense } from 'react';
const AlgorithmStats = lazy(() => import('./components/AlgorithmStats'));
const InteractiveLegend = lazy(() => import('./components/InteractiveLegend'));
// In component:
<Suspense fallback={<Loading />}>
<AlgorithmStats {...props} />
</Suspense>Enhance existing ErrorBoundary with:
- Error logging/reporting
- Retry functionality
- Granular error boundaries per feature
If adding backend features (save/load grids):
import { useMutation } from '@tanstack/react-query';
const { mutate: saveGrid, isPending } = useMutation({
mutationFn: saveGridToServer,
onSuccess: () => toast.success('Grid saved!'),
});Benefits:
- Utility-first approach
- Smaller bundle size (purges unused CSS)
- Consistent design tokens
- Faster development
Migration approach:
- Install Tailwind and configure
- Convert component-by-component
- Keep CSS variables for theming
- Remove old CSS files after migration
src/components/ui/
├── Button.tsx
├── Select.tsx
├── Card.tsx
├── Slider.tsx
├── Badge.tsx
└── index.ts
- Add proper ARIA attributes to grid
- Ensure keyboard navigation works
- Add screen reader announcements for algorithm progress
- Test with accessibility tools (axe, Lighthouse)
Replace Jest with Vitest (faster, Vite-native):
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
});Target: 100% coverage for algorithm files
// src/algorithms/__tests__/BFS.test.ts
describe('BFS', () => {
it('finds shortest path in empty grid', () => {});
it('returns null when path is blocked', () => {});
it('handles start equals end', () => {});
it('explores in correct order', () => {});
});// src/components/__tests__/Grid.test.tsx
describe('Grid', () => {
it('renders correct number of cells', () => {});
it('toggles wall on click', () => {});
it('drags start/end nodes', () => {});
});// e2e/visualization.spec.ts
test('complete algorithm visualization flow', async ({ page }) => {
await page.goto('/');
await page.click('[data-testid="algorithm-bfs"]');
await page.click('[data-testid="visualize"]');
await expect(page.locator('.cell-path')).toBeVisible();
});FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm run test
- run: npm run build// package.json
{
"scripts": {
"prepare": "husky"
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,md,json}": ["prettier --write"]
}
}- Jump Point Search (JPS)
- Bidirectional BFS
- IDA* (Iterative Deepening A*)
- Weighted grid support for Dijkstra
- Undo/Redo for wall placement
- Save/Load grid configurations to localStorage
- Algorithm comparison mode (side-by-side)
- Step-by-step execution with pause/resume
- Export visualization as GIF
- Add service worker for offline usage
- Enable install prompt
- Cache static assets
- Phase 1.1: Migrate to Vite (foundation for everything else)
- Phase 1.2: Enable TypeScript strict mode
- Phase 2.1: Fix all type errors
- Phase 2.2-2.4: Refactor components and extract hooks
- Phase 3.1-3.2: Optimize rendering and state
- Phase 6: Add testing infrastructure
- Phase 5: Styling modernization
- Phase 7: DevOps improvements
- Phase 4 & 8: Advanced patterns and features
src/
├── components/
│ ├── App.tsx
│ ├── Toolbar/
│ ├── Visualization/
│ ├── Stats/
│ └── ui/
├── hooks/
│ ├── useAlgorithmExecution.ts
│ ├── useMazeGeneration.ts
│ └── useVisualization.ts
├── store/
│ └── gridStore.ts
├── algorithms/
│ ├── index.ts
│ ├── BaseAlgorithm.ts
│ ├── AStar.ts
│ ├── BFS.ts
│ ├── DFS.ts
│ ├── Dijkstra.ts
│ ├── GBFS.ts
│ └── MazeGenerator.ts
├── types/
│ ├── algorithm.types.ts
│ ├── grid.types.ts
│ └── animation.types.ts
├── workers/
│ └── algorithm.worker.ts
├── styles/
│ └── globals.css
└── utils/
├── animation.ts
└── grid.ts
# Development
npm run dev # Start Vite dev server
npm run build # Production build
npm run preview # Preview production build
# Quality
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run lint:fix # ESLint with auto-fix
npm run format # Prettier
# Testing
npm run test # Run Vitest
npm run test:ui # Vitest UI
npm run test:cov # Coverage report
npm run e2e # Playwright E2E tests