Case study

Plekkie

Carpooling for sports teams, work outings, festivals and school trips. One link in the group chat, and everyone sees who rides with whom. The app is in Dutch.

Role
Creator. Designed, built and shipped together with Kevin Steba.
Owned
Product, Design, Frontend, Database and policies, Realtime, Deployment
Stack
Next.js, TypeScript, Tailwind CSS, Supabase, Postgres, Vercel
Status
Live since 12 September 2026

Try itRuns in your browser with sample data

Away game, Saturdayplekkie.io/r/K7PX3M

MarkUtrecht Centraal, 09:15
LisaAmersfoort, 09:30

Tap an empty seat to join a car.

01 The problem

Who still has a seat?

Carpooling for a group event gets arranged in a group chat. Someone asks who can drive, a few people reply, and twenty messages later nobody knows which car is full. Plekkie replaces that thread with one page that shows every car and every seat.

02 What it does

A ride room you open from a link.

One person creates a ride and shares the link. Drivers add their car with the number of seats, a departure time and a place. Everyone else taps an empty seat on a top-down car plan. People without a seat show up in a list, so drivers can see who still needs a ride. Recent rides are remembered on the device.

  • No account
  • No app
  • Share by link
  • Live seat map
  • 4, 5 or custom seats
  • Driver blocks seats
  • Looking for a seat
  • Ride history
  • Feedback button
  • Automatic cleanup

03 How it works

Create, share, drive, sit.

  1. rooms.insert()

    01 Create a ride

    Give it a name and an optional date. Plekkie returns a six-character code without look-alike characters like 0 and O.

  2. plekkie.io/r/CODE

    02 Share the link

    Paste it in the group chat. Whoever opens it types a name once, which is kept in the browser. No sign-up.

  3. cars.insert()

    03 Drivers add a car

    Seats, departure time and place. The driver takes seat one and can block seats that are not available.

  4. passengers.insert()

    04 Everyone picks a seat

    Tap an empty seat on the car plan. Pick another and you move. Everyone else sees it straight away.

04 Technical decisions

No login, so the database holds the line.

Without accounts there is no user to check permissions against. The room code is the key instead. The app sends it in an x-room-codes header on every request, and row level security in Postgres only returns rides whose code was sent, at most ten per request. Creating a ride is open to anyone. Changing or deleting a ride through the public API is not.

Two people tapping the same seat is handled by a unique constraint on car and seat. The second insert fails and that person gets a short message. Live updates come from a database trigger that sends an empty signal to the room channel, after which each open page fetches the room again through the same policies. No ride data goes out over the broadcast.

A daily cron job removes rides seven days after their date, or after 30 days without activity.

Architecture Plekkie ride room
  1. Clients
    • Group chat link
    • Phones in the room
  2. Frontend
    • Next.jsVercel
  3. Database
    • Row level securityroom code header
    • Unique seatcar + seat
  4. Realtime
    • Triggerempty signal
    • pg_crondaily cleanup

A write passes the policies, a trigger signals the room channel, and every open page fetches the room again.

05 What we built

Product, interface and database.

Kevin Steba and I came up with Plekkie and built it together. It launched on 12 September 2026. That covers the idea, the Dutch landing page and the room interface with its car plans, the Supabase schema with its policies, triggers and cleanup job, and the deployment on its own domain.

  • Product
  • Design
  • Frontend
  • Database and policies
  • Realtime
  • Deployment

06 Live

The real plekkie.io, embedded.

plekkie.io

Embedded live. Desktop at 1280px, mobile at 390px.

07 Why it matters

Leaving out the login is a security decision.

Skipping accounts makes joining a ride one tap. It also means the usual protection is gone, so it has to live somewhere else. Here it lives in the database, where it applies to every request the app makes.

Also shippedPouletje.nl, two weeks to production