| name | policyengine-app |
| description | PolicyEngine React web application - the user interface at policyengine.org |
PolicyEngine App
The PolicyEngine App is the React-based web application that users interact with at policyengine.org.
For Users 👥
What is the App?
The app at policyengine.org provides:
- Interactive household calculator
- Policy reform creator
- Population impact analysis
- Blog and research hub
Access: https://policyengine.org
App Features
Calculator:
- Enter household details
- See tax and benefit calculations
- Visualize marginal tax rates
- Compare scenarios
Policy designer:
- Browse all parameters
- Create custom reforms
- Share via URL
- Download charts
Research hub:
- Read policy analysis
- Explore modeled programs
- Access documentation
For Analysts 📊
Understanding App URLs
Reform URLs encode all policy changes in the query string, allowing sharing and reproducibility.
Example URL:
policyengine.org/us/policy?
focus=policyOutput.policyBreakdown&
reform=67696&
region=enhanced_us&
timePeriod=2025&
baseline=2
Parameters:
focus- Which section to displayreform- Reform ID from databaseregion- Geographic scope (enhanced_us, CA, congressional districts)timePeriod- Year of analysisbaseline- Baseline policy ID
Embedding PolicyEngine
iFrame integration:
<iframe
src="https://policyengine.org/us/household?embedded=true"
width="100%"
height="800">
</iframe>
Parameter:
embedded=true- Removes navigation, optimizes for embedding
URL Structure
Household calculator:
/us/household?household=12345
/uk/household?household=67890
Policy page:
/us/policy?reform=12345
/uk/policy?reform=67890
Research/blog:
/us/research/article-slug
/uk/research/article-slug
For Contributors 💻
Repository
Location: PolicyEngine/policyengine-app
Clone:
git clone https://github.com/PolicyEngine/policyengine-app
cd policyengine-app
Current Architecture
To see current structure:
tree src/ -L 2
# Key directories:
ls src/
# - pages/ - Page components
# - applets/ - Reusable UI modules
# - api/ - API integration
# - controls/ - Form controls
# - layout/ - Layout components
# - posts/ - Blog posts
# - routing/ - Routing configuration
# - hooks/ - Custom React hooks
# - data/ - Static data
Technology Stack
Current dependencies:
# See package.json for versions
cat package.json
# Key dependencies:
# - React 18
# - React Router v6
# - Plotly.js
# - Ant Design
# - axios
React Patterns (Critical)
✅ Functional components only (no classes):
// CORRECT
import { useState, useEffect } from "react";
export default function TaxCalculator({ income }) {
const [tax, setTax] = useState(0);
useEffect(() => {
calculateTax(income).then(setTax);
}, [income]);
return <div>Tax: ${tax}</div>;
}
❌ Class components forbidden:
// WRONG - Don't use class components
class TaxCalculator extends Component {
// ...
}
To find component examples:
# Reference components
ls src/pages/
ls src/applets/
# See a complete page
cat src/pages/HouseholdPage.jsx
State Management
No global state (Redux, Context) - lift state up:
// Parent manages state
function PolicyPage() {
const [reform, setReform] = useState({});
const [impact, setImpact] = useState(null);
return (
<>
<PolicyEditor reform={reform} onChange={setReform} />
<ImpactDisplay impact={impact} />
</>
);
}
To see state patterns:
# Find useState usage
grep -r "useState" src/pages/ | head -20
API Integration
To see current API patterns:
cat src/api/call.js # Base API caller
cat src/api/variables.js # Variable metadata
cat src/api/parameters.js # Parameter metadata
Standard pattern:
import { api call } from "api/call";
// Fetch data
const result = await call(
`/us/calculate`,
{ household: householdData },
"POST"
);
Routing
To see current routing:
cat src/routing/routes.js
# Routes defined with React Router v6
# See examples:
grep -r "useNavigate" src/
grep -r "useSearchParams" src/
URL parameters:
import { useSearchParams } from "react-router-dom";
const [searchParams, setSearchParams] = useSearchParams();
// Read
const reformId = searchParams.get("reform");
// Update
setSearchParams({ ...Object.fromEntries(searchParams), reform: newId });
Custom Hooks
To see PolicyEngine-specific hooks:
ls src/hooks/
# - useCountryId.js - Current country
# - useDisplayCategory.js
# - etc.
Usage:
import { useCountryId } from "hooks/useCountryId";
function Component() {
const [countryId, setCountryId] = useCountryId();
// countryId = "us", "uk", or "ca"
}
Charts and Visualization
Plotly integration:
# See chart components
ls src/pages/policy/output/
# Reference implementation
cat src/pages/policy/output/EconomyOutput.jsx
Standard Plotly pattern:
import Plot from "react-plotly.js";
const layout = {
font: { family: "Roboto Serif" },
plot_bgcolor: "white",
// PolicyEngine branding
};
<Plot
data={traces}
layout={layout}
config={{ displayModeBar: false }}
/>;
Blog Posts
To see blog post structure:
ls src/posts/articles/
# Read a recent post
cat src/posts/articles/harris-eitc.md
Blog posts:
- Written in Markdown
- Stored in
src/posts/articles/ - Include metadata (title, date, authors)
- Follow policyengine-writing-skill style
Adding a post:
# Create new file
# src/posts/articles/my-analysis.md
# Add to index (if needed)
# See existing posts for format
Styling
Current styling approach:
# See style configuration
ls src/style/
# Colors
cat src/style/colors.js
# Ant Design theme
cat src/style/theme.js
PolicyEngine colors:
- Teal:
#39C6C0(primary accent) - Blue:
#2C6496(charts, links) - Dark gray:
#616161(text)
Testing
To see current tests:
ls src/__tests__/
# Run tests
make test
# Test pattern
cat src/__tests__/example.test.js
Testing libraries:
- Jest (test runner)
- React Testing Library (component testing)
- User-centric testing (not implementation details)
Development Server
Start locally:
make debug
# Opens http://localhost:3000
Environment:
# Environment variables
cat .env.example
# Config
ls src/config/
Building and Deployment
Build:
make build
# Creates optimized production build
Deployment:
# See deployment config
cat netlify.toml # or appropriate hosting config
Component Patterns
Standard Component Structure
To see well-structured components:
# Example page
cat src/pages/HouseholdPage.jsx
# Example applet
cat src/applets/PolicySearch.jsx
Pattern:
import { useState, useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import { useCountryId } from "hooks/useCountryId";
export default function MyComponent({ prop1, prop2 }) {
// 1. Hooks first
const [state, setState] = useState(initialValue);
const [countryId] = useCountryId();
const [searchParams] = useSearchParams();
// 2. Effects
useEffect(() => {
// Side effects
}, [dependencies]);
// 3. Event handlers
const handleClick = () => {
setState(newValue);
};
// 4. Render
return (
<div>
{/* JSX */}
</div>
);
}
Component Size Limit
Keep components under 150 lines after formatting.
If component is too large:
- Extract sub-components
- Move logic to custom hooks
- Split into multiple files
To find large components:
# Find files >150 lines
find src/ -name "*.jsx" -exec wc -l {} \; | sort -rn | head -20
File Naming
Components: PascalCase.jsx
HouseholdPage.jsxPolicySearch.jsxImpactChart.jsx
Utilities: camelCase.js
formatCurrency.jsapiUtils.jschartHelpers.js
Hooks: camelCase.js with 'use' prefix
useCountryId.jsusePolicy.js
Common Development Tasks
Task 1: Add New Page
See page structure:
cat src/pages/HouseholdPage.jsxCreate new page:
// src/pages/MyNewPage.jsx export default function MyNewPage() { return <div>Content</div>; }Add route:
# See routing cat src/routing/routes.js # Add your route following the pattern
Task 2: Add New Chart
See chart examples:
ls src/pages/policy/output/ cat src/pages/policy/output/DistributionalImpact.jsxCreate chart component:
import Plot from "react-plotly.js"; export default function MyChart({ data }) { return ( <Plot data={traces} layout={{ font: { family: "Roboto Serif" }, plot_bgcolor: "white" }} /> ); }
Task 3: Add Blog Post
See post structure:
cat src/posts/articles/harris-eitc.mdCreate post:
# Create markdown file # src/posts/articles/my-analysis.md # Follow policyengine-writing-skill for styleImages:
# Store in public/images/posts/ # Reference in markdown
API Integration Patterns
Fetching Data
To see API call patterns:
cat src/api/call.js
Standard pattern:
import { call } from "api/call";
const fetchData = async () => {
const result = await call(
`/us/calculate`,
{ household: data },
"POST"
);
return result;
};
Loading States
Pattern:
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
useEffect(() => {
setLoading(true);
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [dependencies]);
if (loading) return <Spin />;
if (error) return <Error message={error} />;
return <Data data={data} />;
Performance Patterns
Code Splitting
To see code splitting:
grep -r "React.lazy" src/
Pattern:
import { lazy, Suspense } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
function Page() {
return (
<Suspense fallback={<Spin />}>
<HeavyComponent />
</Suspense>
);
}
Memoization
Use React.memo for expensive components:
import { memo } from "react";
const ExpensiveChart = memo(function ExpensiveChart({ data }) {
// Only re-renders if data changes
return <Plot data={data} />;
});
Accessibility
Requirements:
- Semantic HTML elements
- ARIA labels for complex widgets
- Keyboard navigation
- Color contrast (WCAG AA)
To see accessibility patterns:
grep -r "aria-" src/
grep -r "role=" src/
Country-Specific Features
Country Switching
To see country switching:
cat src/hooks/useCountryId.js
Usage:
import { useCountryId } from "hooks/useCountryId";
function Component() {
const [countryId] = useCountryId(); // "us", "uk", or "ca"
// Load country-specific data
const data = countryId === "us" ? usData : ukData;
}
Country-Specific Content
Conditional rendering:
{countryId === "us" && <USSpecificComponent />}
{countryId === "uk" && <UKSpecificComponent />}
To find country-specific code:
grep -r "countryId ===" src/
Development Workflow
Local Development
Start dev server:
make debug
# App runs on http://localhost:3000
# Connects to production API by default
Connect to local API:
# See environment configuration
cat src/config/environment.js
# Or set environment variable
REACT_APP_API_URL=http://localhost:5000 make debug
Testing
Run tests:
make test
Watch mode:
npm test -- --watch
Coverage:
npm test -- --coverage
Linting and Formatting
Format code (critical before committing):
make format
# Or manually
npm run lint -- --fix
npx prettier --write .
Check linting (CI check):
npm run lint -- --max-warnings=0
Current Implementation Reference
Component Structure
To see current page structure:
ls src/pages/
# - HouseholdPage.jsx
# - PolicyPage.jsx
# - HomePage.jsx
# - etc.
To see a complete page:
cat src/pages/PolicyPage.jsx
API Call Patterns
To see current API integration:
cat src/api/call.js # Base caller
cat src/api/variables.js # Variable metadata fetching
cat src/api/parameters.js # Parameter metadata fetching
Routing Configuration
To see current routes:
cat src/routing/routes.js
Form Controls
To see PolicyEngine-specific form controls:
ls src/controls/
# - InputField.jsx
# - SearchParamControl.jsx
# - etc.
Chart Components
To see chart implementations:
ls src/pages/policy/output/
# - BudgetaryImpact.jsx
# - DistributionalImpact.jsx
# - PovertyImpact.jsx
# - etc.
Reference chart:
cat src/pages/policy/output/DistributionalImpact.jsx
Multi-Repository Integration
How App Relates to Other Repos
policyengine-core (engine)
↓
policyengine-us, policyengine-uk (country models)
↓
policyengine-api (backend)
↓
policyengine-app (you are here)
Understanding the stack:
- See
policyengine-core-skillfor engine concepts - See
policyengine-us-skillfor what variables/parameters mean - See
policyengine-api-skillfor API endpoints the app calls
Blog Posts Reference Country Models
Blog posts often reference variables:
# Posts reference variables like "income_tax", "ctc"
# See policyengine-us-skill for variable definitions
cat src/posts/articles/harris-eitc.md
Common Development Tasks
Task 1: Add New Parameter to UI
Understand parameter:
# See parameter in country model cd ../policyengine-us cat policyengine_us/parameters/gov/irs/credits/ctc/amount/base_amount.yamlFind similar parameter in app:
cd ../policyengine-app grep -r "ctc.*amount" src/pages/policy/Add UI control following pattern
Task 2: Add New Chart
See existing charts:
cat src/pages/policy/output/DistributionalImpact.jsxCreate new chart component
Add to policy output page
Task 3: Fix Bug in Calculator
Find relevant component:
# Search for the feature grep -r "keyword" src/pages/Read component code
Make fix following React patterns
Test with dev server:
make debug
Build and Deployment
Production build:
make build
# Creates optimized bundle in build/
Deployment:
# See deployment configuration
cat netlify.toml # or appropriate config
Environment variables:
# React env vars must have REACT_APP_ prefix
REACT_APP_API_URL=https://api.policyengine.org
# Or use config file pattern (recommended)
cat src/config/environment.js
Style Guide
Follow policyengine-standards-skill for:
- ESLint configuration
- Prettier formatting
- Component size limits
- File organization
Follow policyengine-writing-skill for:
- Blog post content
- Documentation
- UI copy
Resources
Repository: https://github.com/PolicyEngine/policyengine-app Live app: https://policyengine.org Staging: https://staging.policyengine.org (if applicable)
Related skills:
- policyengine-api-skill - Understanding the backend
- policyengine-us-skill - Understanding variables/parameters
- policyengine-writing-skill - Blog post style
- policyengine-standards-skill - Code quality