Introduction
We're back. After our four-week sprint that took Luminary from a blank repository to a fully functional community platform built in vanilla HTML, CSS, and JavaScript, we regrouped to migrate the entire project to a modern framework.
Luminary is a community-powered directory and news platform celebrating women who are driving change across all fields — business, science, arts, activism, sport, technology, agriculture, education, and more. It allows anyone to nominate or self-submit, is searchable by name and field, and surfaces daily and weekly news of women making impact globally.
For this two-week sprint, we moved Luminary off the vanilla stack and onto a proper application architecture. I would be writing about the migration in general, my role in it, and the lessons I picked up along the way.
The Migration
The old Luminary lived as a set of standalone HTML pages, which we had wired up with its own script and stylesheet. It worked fine, but as the project grows, sharing logic, keeping styles consistent, and integrating with the backend cleanly is foreseen to become hectic.
This sprint we restructured the project into a Turborepo monorepo with two apps — a Next.js (App Router) + TypeScript frontend and an Express + Supabase backend. On the frontend we adopted Tailwind for styling,
Technical Implementation Breakdown
Shared form primitives: Extended the commonformmodule intoFormField,TextField,FormLabel, and a Base UISelectFieldso labels and inputs are defined once and reused everywhere instead of being hand-written per form.
Data out of components: Moved form constants — initial state, tabs, and field options — into the data layer, keeping the components focused on rendering.
Design system consistency: Replaced raw<button>elements across the tabs, link fields, and collapsible sections with the customButtoncomponent, and added a reusableDrawerwrapper around Base UI's drawer.
Service layer: Split the logic into aNominationServiceand a standaloneUploadService, with a privatetoPayloadmethod and a shared error utility for consistent messaging.
Submission flow: Swapped manualtry/catchhandling for TanStack Query's mutationonError, and conformed API errors to a typed response shape.
Challenges & Resolutions
Turning imperative code into components: The original form was built with direct DOM manipulation, so I had to rethink it as composable React components and lift the shared pieces — labels, inputs, button, drawer — into reusable primitives rather than porting the markup one-to-one.
Acting on review feedback: A good portion of the week was responding to PR reviews from
Technical Implementation Breakdown
Profile route: Created/directory/[id]as an async server component and rebuilt the oldprofile.htmlsections — header, about, and related links — as components usingnext/imageand the original decorative photo frame.
Shared mapper: Added a singletoNomineeProfilemapper feeding both the directory list and a newgetProfileById, normalizing inconsistent URL fields coming back from the API.
Image configuration: Allow-listed the remote image hosts in the Next.js config so profile and avatar images render through the image pipeline.
Challenges & Resolutions
Profiles returning "not found": The directory cards were keyed on the nominee id while the detail endpoint looks records up by nomination id, so links never resolved. I aligned both on the nomination id through the shared mapper.
The backend wouldn't boot locally: It was missing its environment file, and once that was added the Supabase client still threw — Node.js below version 22 has no native WebSocket, which the client requires, so I added awsfallback to provide one.
A request black hole: Even with the server running, requests never reached it. Port 5000 turned out to be reserved at the OS level on my computer, so the kernel was answering instead of the app. Moving local dev to a free port fixed it instantly, interestingly this was an issue I experienced in the original project stack as well, but had to debug and resolve again as I had forgotten, higlighting the importance of documentation in projects, silly me.
What I Learned This Week
This week, from migrating the profile pages, I learnt new methods of integrating a typed frontend with a real API across server components, the value of a single mapping layer between API and UI shapes, and the importance of documentation across project sprints.
Conclusion
Two weeks in, Luminary has a more solid foundation — a typed, component-driven frontend, the hard part of the move is behind us. For now, at least, onto the next.
Github Repository
-Product Lead 2:
-Lead Maintainer:
-Engineering Lead:↗ Original-Artikel auf dev.to lesenVollständiger Original-BerichtAusführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
SOCIAL SHARE CARD GENERATOR