I Built a Full Local Business Directory in a Day. Here's How.
From blank folder to a fully functioning local business directory with maps, payments, user accounts and an admin panel — here's everything that went into building ThanetDirectory.com in a single session with Claude Code.

Yesterday I built a blog. Today I built a business.
ThanetDirectory.com is a full local business directory for the Isle of Thanet covering Margate, Ramsgate, Broadstairs, Birchington and ten surrounding areas. It has an interactive map, business listings with photos, a three tier subscription model, Stripe payments, user accounts, a claim flow for business owners, an admin panel, a newsletter signup and a local news feed pulling from the Isle of Thanet News RSS.
All of it built in one session with Claude Code.
Here is everything that went into it.
The Tech Stack
Next.js 15 with the App Router handles the framework. It was chosen because it supports server components for SEO and fast page loads, API routes for secure server-side operations, and dynamic rendering all in one place without needing a separate backend. React 19 and TypeScript throughout.
Supabase handles the database and authentication. PostgreSQL with Row Level Security policies, Supabase Auth for email and password accounts plus Google OAuth, and Supabase Storage for business photo uploads. The SSR package handles server-side session management.
Tailwind CSS v4 for styling with custom colour tokens built around the site's deep teal and warm amber palette. React Hook Form with Zod v4 for all form validation. Leaflet with React Leaflet for the maps using free OpenStreetMap tiles with no API key required. Stripe for payments. Resend for email.
Everything That Was Built
The homepage has a hero with a search bar, a featured businesses carousel, a browse by category grid with twelve categories, an interactive map showing all businesses with colour coded pins, a local news section pulling from the Isle of Thanet News RSS feed, town links, a newsletter signup and a call to action for businesses to add their listing.
The directory page has a search bar with debouncing, a filter sidebar covering category, area and tags grouped into six categories, three view modes in grid, list and map, pagination and active filter chips. The search updates the URL so each query triggers a fresh server render rather than filtering stale cached data.
Individual business pages have the full listing detail, a photo gallery, opening hours, contact information, social media links, an enquiry form, a map showing the single location and a claim button for unclaimed listings.
Category pages list all businesses in that category with featured listings shown first. Town pages for the four main towns have editorial content about the area alongside their business listings. There are also pages for about, contact, local news, useful local links and account settings.
The claim flow starts when a business owner clicks claim on a listing. They are taken to a claim form with a business name typeahead that searches for matching unclaimed listings as they type. They sign in with Google or email and password, with the password fields hidden automatically when already authenticated via Google. The claim is submitted, goes into a pending queue and the admin approves or rejects it from the admin panel.
The Database
Six tables cover everything the site needs.
Businesses is the core table with name, slug, description, category, town, tags stored as an array, address and coordinates, contact details, opening hours in JSON format, photos as an array of storage URLs, social links, status tracking from unclaimed through to featured, view count and subscription plan.
Categories covers the twelve business categories each with an emoji icon, colour and description. Profiles extends Supabase Auth users with a role field distinguishing business owners from administrators. Claims links users to listings they want to take ownership of. Newsletter subscribers tracks email signups with a source field noting where they came from. Enquiries stores messages sent to businesses through the contact form on their listing.
The Pricing Model
Three tiers handle monetisation.
Free listings get the basics. Name, address, phone, category, appearance in search and on the map, and the customer enquiry form. The enquiry form being free on every listing is deliberate. It gives business owners a tangible reason to claim even on the free tier because they get direct leads. When those leads come in, upgrading becomes an easy conversation.
Standard at £4.99 a month unlocks photos up to ten, full description, opening hours editor and social media links.
Featured at £9.99 a month adds the featured badge, placement at the top of category results and inclusion in the homepage carousel.
Feature gating is enforced both at the UI level with blurred overlays on locked sections in the dashboard and at the API level where the route checks subscription plan before writing gated fields. Stripe's hosted Checkout and Customer Portal handle all payment UI, PCI compliance and subscription management. The webhook updates the plan field on the business record when subscription events come in.
The Map
Leaflet with React Leaflet was chosen over Google Maps because it is completely free. OpenStreetMap tiles require no API key and no billing account. Data quality for UK towns is excellent. All map components use dynamic imports with server side rendering disabled because Leaflet requires the browser DOM which does not exist during Next.js server rendering. Business coordinates default to the centre of Thanet when no specific coordinates are provided.
Errors Encountered Along the Way
This is the part I always find most useful to read so here is everything that went wrong.
Admin operations were silently failing. The Supabase SSR client when used with the service role key was interfering with reads and writes and returning empty data without errors. The fix was to use the base Supabase client directly for any operation that needs elevated permissions.
An invalid UUID error was appearing on category selection. The seed data used non-standard UUID formats and empty strings were being passed when no category was selected. Fixed with a Zod preprocessor that converts empty strings to null before validation runs.
The claim form was blocking submission for users already logged in via Google because the password field was still required by the validation schema even when authenticated. Fixed by making the schema dynamic based on login state.
Zod v4 changed how record schemas work. The old single argument syntax broke. Updated to the new two argument version requiring explicit key and value schemas.
Search was not finding newly added businesses. Two problems were stacked. The initial full text search implementation referenced a database column that had never been created, so Supabase silently returned nothing. On top of that, Next.js was serving a cached version of the directory page that predated the new listing. Fixed by switching to a simpler name search approach and forcing fresh data on every request.
What Is Still Outstanding
The code is complete but a few configuration steps remain before launch. Google OAuth needs enabling in the Supabase dashboard with credentials from Google Cloud Console. All production environment variables need setting in Vercel. The photo storage bucket needs creating via a migration script. Email notifications for claim approvals need wiring up. The seed data covers 36 businesses across all categories but wants expanding to 100 or more before launch.
Full text search is on the list. The current name search works but a proper PostgreSQL full text search across name, description, category and tags simultaneously will make the directory significantly more useful. That goes in before launch.
What Comes Next
The directory is not live yet. Before it goes live there is content to add, seed businesses to expand, and the outstanding configuration to complete. Once it is live the plan is to grow listings through local Facebook groups, offer free claiming to the first businesses who sign up, and document the whole growth process here.
That is the next post.
Follow along at madewithprompts.co.uk