Next.jsReactFirebaseGemini AILeafletFramer MotionCivic Tech

Port Laken is an AI-powered civic portal for a fictional city inspired by Port Angeles, WA. I built it because I kept looking at city websites and thinking, bro, why does finding basic local information have to be this hard? So instead of making another basic school project, we built what we thought a city website should actually feel like: clean, fast, searchable, interactive, and actually designed around residents. It uses Next.js App Router, Firebase, Gemini, Leaflet, and a bunch of other modern web tooling. We open-sourced the whole thing and actually showed it to city governments around Washington. The coolest part wasn't the TSA placement. It was seeing municipalities take UI and information-architecture ideas from the project and think about how they could modernize their own sites.

Most city websites are still built around departments, PDFs, giant navigation menus, and making residents figure out where information lives. That's backwards. If someone needs food assistance, healthcare, an emergency resource, or transportation info, they shouldn't need to understand the city's organizational structure first. Port Laken flips that around. It's built around what residents are actually trying to do. The resource directory puts useful local services in one place, the AI assistant lets people ask questions normally, maps make transportation easier to understand, and alerts surface things residents actually need to know. We also made the project open-source and took it beyond TSA by showing it to city governments around WA. The goal was basically: here's what a city portal could look like if we stopped accepting 2012-era government UX as the default.

  • The AI assistant cannot hallucinate civic information. Being confidently wrong about an emergency resource is way worse than saying "I don’t know."
  • Community-submitted resources need moderation, otherwise the directory becomes a spam box immediately.
  • Leaflet depends on `window`, so it cannot just be imported during Next.js server rendering.
  • Protected actions need to send users back to exactly where they were instead of dumping them on some random dashboard after signing in.
  • The site had to be more than a pretty mockup. It needed enough real functionality that an actual city could look at it and go, "wait, we could actually use some of this."

Built it as something cities could actually steal from

We intentionally open-sourced Port Laken and showed it to city governments around Washington instead of letting it die as a TSA submission. The point was to give cities an actual working example they could click through, inspect, and pull ideas from. That included the navigation, resource directory, page layouts, maps, alerts, and general resident-first UI philosophy.

Reason:
If you want to convince a city that its website could be better, a Figma mockup or a PowerPoint deck only gets you so far. Give them a working website and suddenly the conversation is way more concrete.

Tradeoff:
Port Laken is obviously not production municipal infrastructure. It is a reference implementation. The point is showing what is possible and giving cities ideas they can actually adapt.

Designed around residents, not city departments

Instead of making residents navigate a giant list of departments, Port Laken organizes information around actual problems people have: finding food, healthcare, emergency services, transportation, and other local resources.

Reason:
Nobody wakes up thinking "I need to access the Department of Human Services." They think "I need help finding food." The website should understand that difference.

Tradeoff:
This takes more work to organize and categorize than just copying a government org chart into a navbar.

RAG-style prompt grounding without a vector database

A local buildAiContext function builds the AI context on every query using the user’s question, a PAGE_CONTEXTS map covering the app, and the JSON for currently relevant civic resources. Gemini 2.5 Flash is told to only answer from that context and returns structured output instead of just dumping free-form text.

Reason:
The dataset is small. Building a whole vector database and embedding pipeline would’ve been infrastructure for the sake of infrastructure. Just give the model the relevant data.

Tradeoff:
This obviously doesn’t scale to a massive resource database. Every query can still send a pretty chunky chunk of JSON.

Seed data + moderated community submissions

The directory starts with curated RESOURCES data but also supports community submissions through Firestore. Submissions start as pending and only become visible after approval.

Reason:
A directory with zero resources is useless, but letting literally anyone publish directly is also a terrible idea. Seed it, then let the community expand it safely.

Tradeoff:
Now there are two sources of truth, so reads and AI context have to merge and dedupe them properly.

Dynamically imported Leaflet maps

Maps & Transport uses react-leaflet with SSR disabled so Leaflet only initializes in the browser.

Reason:
Leaflet wants `window`, Next.js server rendering does not have `window`. Pretty straightforward.

Tradeoff:
The map has a small client-side loading gap instead of being included in the initial server-rendered HTML.

callbackUrl auth redirects

When someone tries to submit a resource or manage alerts without being signed in, they get sent to /sign-in with a callbackUrl. After authenticating, they land right back where they started.

Reason:
If someone is trying to submit a food pantry listing, making them sign in and then hunt through the dashboard for the page again is just unnecessary friction.

Tradeoff:
Every new protected flow has to handle the callback correctly or the UX gets annoying fast.

  • Placed 8th at the WTSA 2026 state conference.
  • Open-sourced the project and showed it to city governments around Washington, turning a TSA project into an actual civic-tech reference instead of letting it sit in a GitHub repo forever.
  • City governments around WA took UI and information-architecture inspiration from Port Laken while thinking through how to modernize their own municipal websites.
  • Built a working Gemini-powered civic assistant that is grounded in local page context and resource data instead of just letting an LLM freestyle government information.
  • Shipped a full civic portal: moderated resource directory, alerts with Resend email dispatch, interactive Leaflet maps, authentication, and resident-focused navigation.
  • Released the implementation publicly so other developers and municipalities can actually look at the code and ideas instead of taking our word for it.

  • The coolest validation wasn’t getting 8th at TSA. It was showing the thing to actual city governments and having them take UI ideas from it for their own sites.
  • Open-source is way more useful when you build something people can actually take. A city can look at a working portal and say "we should do that" way easier than they can from a 30-slide presentation.
  • For small datasets, you don’t always need some insane RAG architecture. Sometimes just giving the model the right data and aggressively constraining what it can say is enough.
  • Community submissions only work if there is a moderation layer. Pending → approved is boring, but it matters.
  • Making the city fictional gave us room to experiment without pretending we were shipping production government infrastructure. We could go crazy with the UX while still making something real cities could learn from.
  • At the end of the day, I cared less about making a cool TSA website and more about whether someone outside the competition would actually want to use the ideas. That ended up being the most interesting part of Port Laken.