The Visita Development Philosophy
The mental model for building scalable Next.js features in a civic intelligence platform.Core Principle: We build critical infrastructure, not social networks. Every feature must serve awareness and civic participation, not engagement for its own sake.
1️⃣ Start with the Role of the Page (WHY does it exist?)
Before writing code, ask:Core Questions
- What job does this page do?
- Who is this page for? (guest, citizen, business, analyst, admin)
- What problem does the page solve?
- Is this a destination page or a support page?
Visita Examples
- Ward Dashboard → Awareness & Action
- Directory Page → Discovery & Connection
- Profile Page → Identity & Reputation
- Settings Page → Control & Safety
- Analyst Dashboard → Intelligence & Oversight
2️⃣ Page vs Layout vs Component (CRITICAL separation)
In Next.js, you should think in three layers:- Layout (persistent structure)
- Page (orchestration)
- Components (capabilities)
Ask:
- What persists across pages? → layout
- What is unique to this route? → page
- What is reusable or self-contained? → component
Visita Example: Ward Structure
3️⃣ Define the Page’s Component Inventory
Before coding, list the components this page needs.Example: Ward Dashboard Page
Component Inventory:WardHeader(title, ward code, last updated)WeatherWidget(current conditions, rain alerts)SignalFeed(crime, alerts, community signals)SignalCard(individual signal display)DirectoryMap(local businesses, services)CouncillorProfile(ward representative info)CommunityActions(active petitions, cleanups)
🔍 Component Questions
For each component, ask:- Does this component appear on other pages?
SignalCardappears in Ward Feed AND Profile History ✅
- Could it appear in the future?
DirectoryMapcould be used in search results ✅
- Is it domain-specific or generic?
WeatherWidgetis domain-specific (ward intelligence)Buttonis generic (UI component)
- Does it need props?
SignalCardneedssignalobject ✅
- Does it manage state?
SignalFeedmanages pagination state ✅
- Where the component should live (
/components/ward/vs/components/ui/) - How reusable it should be (generic API vs specific)
- How flexible its API must be (props design)
4️⃣ Reusability Test (The “Three Contexts” Rule)
Ask: Can this component logically exist in three different contexts?✅ Reusable Component: SignalCard
- Ward Feed (
/ward/[code]) - Profile History (
/profile/signals) - Search Results (
/search?q=crime)
❌ Page-Locked Component: WardHeader
Only appears on ward pages → Keep local to page
5️⃣ Data Responsibility (Who owns the data?)
One of the most common mistakes in component architecture.Ask:
- Does the page fetch data?
- Or does the component fetch data?
- Is the data global, shared, or local?
🎯 Rule: Pages Fetch, Components Render
Exceptions (When Components CAN Fetch):
- Infinite Scroll (loads more on interaction)
- Highly Interactive Widgets (real-time updates)
- Lazy-Loaded Sections (below-the-fold content)
6️⃣ State Ownership (Where does logic live?)
Ask:- What state exists on this page?
- Is the state local or shared?
- What triggers re-renders?
Guiding Principle
State lives as high as necessary, as low as possible
Visita Examples
URL State (Ideal for Filters)
7️⃣ Server vs Client (Next.js-specific)
Every page should answer:- Can this be a Server Component?
- What must be client-side?
- Do I need interactivity here?
Decision Tree
🎯 Default to Server
Opt-in to Client When Needed
Hybrid Approach (Islands of Interactivity)
8️⃣ Performance Questions (Before it’s a problem)
Ask early:- Is this page heavy?
- What can be lazy-loaded?
- What is above-the-fold?
Checklist
1
Step 1: Identify Heavy Components
2
// Heavy components to lazy-load:
- DirectoryMap (Leaflet map + many markers)
- SignalChart (Chart.js or D3 visualization)
- BusinessDirectory (large list with images)
3
Step 2: Use Dynamic Imports
4
// ✅ GOOD: Lazy load heavy components
import dynamic from 'next/dynamic';
const DirectoryMap = dynamic(
() => import('@/components/ward/DirectoryMap'),
{
loading: () => <MapSkeleton />, // Show skeleton while loading
ssr: false // Don't SSR map (browser-only)
}
);
export default function WardPage() {
return (
<div>
<WeatherWidget /> {/* Above-the-fold, load immediately */}
<DirectoryMap /> {/* Below-the-fold, lazy load */}
</div>
);
}
5
Step 3: Optimize Images
6
import Image from 'next/image';
<Image
src={business.logo}
alt={business.name}
width={80}
height={80}
priority={false} // Don't prioritize below-the-fold images
placeholder="blur" // Blur placeholder
blurDataURL="data:image/jpeg;base64,..."
/>
7
Step 4: Virtualize Long Lists
8
// For very long signal feeds (1000+ items)
import { FixedSizeList as List } from 'react-window';
function VirtualizedSignalFeed({ signals }: { signals: Signal[] }) {
return (
<List
height={600}
itemCount={signals.length}
itemSize={120}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<SignalCard signal={signals[index]} />
</div>
)}
</List>
);
}
9️⃣ Access & Security
Every page should answer:- Who can access this page?
- What happens if user is not allowed?
- What data must be protected server-side?
Visita Examples
Server-Side Protection (Critical!)
RLS (Row Level Security)
Enable RLS policies in Supabase for database-level security:🔟 UX & Failure States (Often forgotten)
Ask: What does this page look like when:- Loading?
- Empty?
- Error?
- Partial data?
Define States for Each Page
Skeleton Components
Empty States
Error Boundaries
1️⃣1️⃣ Folder Placement (Reflect the thinking)
A clean structure reflects good thinking:UI Architecture Hierarchy
The three-layer component architecture ensures clear separation of concerns:- UI Primitives (
/components/ui/) — Generic, accessible building blocks - Layout Components (
/components/layout/) — Structural, persistent UI - Domain Components (
/components/ward/,/components/business/) — Visita-specific logic
Component Placement Rules
- Page-specific:
/app/ward/[code]/components/ - Ward domain:
/components/ward/ - Generic UI:
/components/ui/ - Business domain:
/components/business/ - Auth domain:
/components/auth/
1️⃣2️⃣ UI Architecture Principles
Component Hierarchy
Every component should be placed in the appropriate layer:- Page-specific:
/app/ward/[code]/components/— Only used by this page - Domain:
/components/ward/— Shared across ward pages - Layout:
/components/layout/— Structural, persistent UI - UI Primitives:
/components/ui/— Generic, reusable atoms
UI Strategy Alignment
Visita uses a Headless + Utility CSS approach:- Radix UI for behavior and accessibility
- Tailwind CSS for styling control
- shadcn/ui for component templates (we own the code)
- ✅ Full control over design and behavior
- ✅ Accessible by default
- ✅ No vendor lock-in
- ✅ Civic-appropriate aesthetics
Design Philosophy
Visita’s UI must feel:- Civic — Serious, trustworthy, institutional
- Calm — Not flashy or attention-grabbing
- Structured — Clear hierarchy and organization
- Neutral — Data-respecting, not emotionally manipulative
- Accessible — Works for all citizens
- ❌ Over-rounded “startup bubbles”
- ❌ Flashy gradients and animations
- ❌ Engagement-optimized patterns
- ❌ Trendy designs that age poorly
- UI Strategy & Design System — Architecture and philosophy
- UI Primitives Reference — Component APIs and usage
1️⃣3️⃣ Final Developer Checklist (Use this every time)
Before writing code, answer:Page-Level
- What is this page’s purpose? (one-sentence description)
- Who is it for? (guest, citizen, business, analyst, admin)
- What data does it need? (list all data dependencies)
- Is it server or client? (default to server, opt-in to client)
- What are the loading/empty/error states? (define all UX states)
- Is it public or protected? (auth requirements)
Component-Level
- Is this reusable? (three contexts test)
- What props does it need? (design flexible API)
- Does it own state? (local vs page vs global)
- Can it be dumb? (prefer presentational over smart)
- Does it fetch data? (or does page provide data)
- What are its states? (loading, empty, error)
Architecture
- What belongs in layout? (persistent structure)
- What belongs in page? (orchestration, data fetching)
- What belongs in component? (capabilities, rendering)
- Where should it live? (folder placement)
- How is it secured? (RLS, server checks, client guards)
Performance
- Is anything heavy? (maps, charts, large lists)
- What can be lazy-loaded? (below-the-fold content)
- Are images optimized? (Next.js Image, placeholders)
- Is this SEO-critical? (server-render important content)
Security
- Who can access? (public, auth, role-based)
- Server-side checks? (never trust client)
- RLS policies? (database-level security)
- Input validation? (sanitize all inputs)
The Meta-Principle (This is the real lesson)
Pages orchestrate. Components specialize. Layouts persist. Data flows down. State lives low. Security never sleeps.In Visita, we build critical infrastructure for civic intelligence. Every architectural decision should reflect:
- Clarity over cleverness (simple, readable code)
- Security over speed (verify, then trust)
- Awareness over engagement (inform, don’t manipulate)
- Resilience over features (handle failure gracefully)
- Civic value over vanity metrics (solve real problems)
Summary
This development philosophy ensures:- ✅ Scalable architecture (clear separation of concerns)
- ✅ Performance by default (server components, lazy loading)
- ✅ Security first (RLS, server checks, input validation)
- ✅ Great UX (loading states, empty states, error handling)
- ✅ Maintainable code (reusable components, clear structure)
- ✅ Civic alignment (awareness, action, infrastructure mindset)
Status: Active
Applies To: All frontend development
Reviewed: January 2026