Luminary’s journey from a static HTML directory to a modern Next.js application highlights the transformative power of structured architecture and reusable components. After four weeks of building the platform from scratch using vanilla HTML, CSS, and JavaScript, the team took on a two-week migration sprint to adopt a more scalable framework. The move to Next.js with TypeScript, Turborepo, and Tailwind wasn’t just about technical upgrades—it was about future-proofing a platform that celebrates women driving change globally.
The original Luminary platform functioned as a collection of standalone HTML pages, each paired with its own JavaScript and CSS. While effective for initial deployment, this approach posed challenges as the project grew. Maintaining consistent styling, sharing business logic, and integrating with the backend became increasingly complex. The team recognized the need for a structured architecture that could scale seamlessly. The solution? A Turborepo monorepo housing two distinct applications: a Next.js frontend with App Router and TypeScript, and an Express backend with Supabase for data persistence.
Rebuilding the Nominations Flow with React and Design Systems
The migration sprint began with converting the nominations experience from a DOM-driven form into a composable React component system. This wasn’t a simple refactor—it was a complete rethinking of how user input could be modularized and reused across the platform.
The team extended a shared form component library to include primitives like FormField, TextField, and SelectField, all built on top of Base UI for accessibility. These components shared a single source of truth for labels, inputs, and validation logic, eliminating redundancy. A key architectural decision was moving form constants—such as initial state, field options, and validation rules—into the data layer. This separation of concerns allowed components to focus solely on rendering, improving maintainability.
Data fetching and submissions were handled using TanStack Query, replacing manual try/catch blocks with a robust mutation system. The introduction of a typed NominationService and a standalone UploadService further decoupled API concerns from the UI. A shared error utility ensured consistent error messaging across the application, while a private toPayload method standardized the structure of API requests.
Overcoming Imperative Code Patterns
One of the biggest challenges was transforming the original form’s imperative DOM manipulation into declarative React components. The team didn’t just port the markup—it re-architected the entire submission flow. For instance, labels, inputs, buttons, and collapsible sections were replaced with reusable components from the design system. A custom Drawer wrapper was added around Base UI’s drawer to maintain consistency.
Code reviews played a critical role in refining the implementation. Feedback from the repository maintainer prompted adjustments such as adopting design system components, removing manual memoization (thanks to the React Compiler), and centralizing repeated logic into shared utilities.
Connecting Profiles to Live Data
Week two shifted focus from forms to data. The team migrated directory and profile pages to fetch real records from the backend, ensuring the platform delivered live, up-to-date information. This phase involved rebuilding the profile route as a dynamic Next.js server component, complete with asynchronous data fetching.
A shared mapping layer was introduced to normalize inconsistent data structures between the API and the UI. For example, the directory cards were keyed on the nominee ID, while the detail endpoint used nomination IDs. By aligning both systems through a toNomineeProfile mapper, the team resolved broken links and inconsistencies.
Images were another critical consideration. The team configured Next.js to allow-list remote image hosts, ensuring profile and avatar images rendered efficiently through the image pipeline. This setup also enabled the use of next/image for optimized performance.
Debugging Common Development Hurdles
Migrating to a new stack often uncovers environment-specific issues. In this case, the backend failed to boot locally due to a missing environment file. Even after resolving that, the Supabase client threw errors because Node.js versions below 22 lacked native WebSocket support. The solution? Adding a ws fallback to provide WebSocket functionality.
Another unexpected obstacle emerged when requests to the backend were silently dropped. The root cause? Port 5000 was reserved at the OS level, causing the kernel—not the application—to handle incoming traffic. Switching to an alternative port resolved the issue instantly.
Key Takeaways from the Migration
The migration of Luminary’s platform underscored the importance of component-driven development and design system consistency. By abstracting shared logic into reusable components and adopting a typed service layer, the team improved code maintainability and reduced duplication. TanStack Query proved invaluable for managing data fetching and mutations, while TypeScript ensured type safety throughout the application.
The project also highlighted the value of iterative development and code reviews. Feedback from team members helped refine the implementation, ensuring alignment with project conventions and best practices. Looking ahead, the team plans to expand the platform’s features, including enhanced search capabilities and real-time updates, all built on the robust foundation established during this migration.
AI summary
Topluluk platformu Luminary’nin sıfırdan Next.js’e geçiş süreci, karşılaşılan zorluklar ve alınan dersler. Modern mimariye geçişin avantajları ve teknik detaylar.