FoundStep

A home base for indie developers. Portfolio, devlog, and public record of shipped work, all in one place.


Inspiration

I kept starting side projects and watching them die in a Notion doc somewhere. Half-finished. No audience. No record of the work I actually did.

The tools that existed felt built for teams or for influencers. Nothing felt right for a solo dev who just wants a place to park their projects, show their progress, and stop losing momentum between builds.

So I built the thing I wished existed.

What it does

FoundStep is a home base for indie developers. You get a public profile, a wall of your projects, and a clean way to share what you are working on without spinning up a portfolio site from scratch every year.

Each project lives on its own page with a story, links, and visuals. Visitors can actually see what you built and how you built it. Profile views and project views are tracked, so you know when your work is getting attention.

It is a portfolio, a devlog, and a public record of shipped work, all in one place.

How I built it

Frontend

  • Next.js 15 with the App Router and Turbopack
  • React 19, TypeScript, Tailwind v4
  • shadcn/ui (new-york) with a few custom blocks on top

Auth

  • Custom JWT setup using RS256 keys verified through jose
  • Automatic token refresh handled in middleware

API layer

  • Two axios instances: one for the browser (proxied through /api/ext/*) and one for the server (hits the backend directly with cookies)
  • Each service lives in its own folder with separate client and server entry points
  • Zod validation on every response

State

  • Zustand stores, hydrated from the server on first render so the UI never flashes the logged-out version for a signed-in user

Analytics

  • The public wall has its own analytics beacon that fires a lightweight visitor-ID ping on profile and project views, so I can give creators real numbers without pulling in a third-party tracker

Challenges I ran into

Token refresh in middleware was harder than I expected. Getting the refresh flow to behave correctly across server components, client fetches, and the edge runtime took several rewrites before it stopped racing itself.

Hydration was the other big one. Zustand stores that depend on server data will happily cause mismatches if you hydrate them the wrong way. I ended up with a dedicated hydrator component in the root layout that takes server data and pushes it into the stores before anything renders.

Designing a profile page that looks good whether someone has one project or twenty was a quiet challenge too. A lot of the layout work was making empty states feel intentional instead of broken.

Accomplishments that I'm proud of

  • The auth flow feels invisible, which is the whole point. No flashing redirects, no losing your place, no weird logged-out flicker.
  • The public wall loads fast and looks sharp on mobile without a single layout hack.
  • Visitor tracking works without cookies or a third-party script.
  • And honestly, shipping it at all. Most of my past projects never left the local dev server. This one is live.

What I learned

  • Middleware is powerful and also the easiest place to introduce bugs that only show up in production. Write it carefully.
  • Zod schemas pay for themselves the first time a backend response changes shape without warning.
  • Designing for indie devs means respecting their time. Every extra form field is a reason to close the tab.
  • Turbopack is genuinely fast now. The dev loop on this project is the tightest I have had in years.

What's next for FoundStep

  1. Richer project pages with changelog entries, so creators can post updates as they ship instead of rewriting the whole story each time.
  2. Discovery. Right now profiles are isolated islands. I want a way for visitors to stumble onto interesting projects without needing a direct link.
  3. Lightweight monetization for creators who want to sell access, early builds, or support tiers from their FoundStep profile without routing people to a separate platform.
Share this project:

Updates