The first problem with Aluxbound was not technical.
We could not decide what the website should feel like.
I was building it for my cousin's luxury travel business. He knew exactly what a good trip should feel like for a customer. I knew how to turn ideas into a working product. Those are useful skills, but they do not automatically produce a clear brief.
We went back and forth on the visual direction. Then we got stuck on the logo font. A typeface that looked elegant in one version made the brand feel too formal in another. It was one of those decisions that looks tiny in a task list and changes the mood of the entire site.
The same thing happened with the admin panel.
At first, the request sounded simple: add destinations, manage enquiries, create plans, and send payment links. Then the real questions appeared.
What does “add a destination” mean for someone who does not work with websites? Should they paste an image URL? Should activities be entered as a block of JSON? How should they know whether an image will look right? What should happen when they leave a half-finished form?
Those questions shaped the architecture more than any technology choice did.
The first rule: build around the person using it
The admin user should not have to understand the database. So the destination form uses normal fields, repeatable activity cards, file pickers, previews, and a map picker for globe locations.
The database stores activities as JSON because they are repeatable content blocks. The admin never sees that JSON. They enter a title, description, category, and image. The form converts those fields into the structure the server expects.
The same idea applies to media. The admin chooses an image or video from their device, sees a preview, and saves it with the destination. They do not need to know how Cloudinary URLs or transformations work.
That sounds obvious now. It took time to arrive there.
Why mobile came first
The business is run from a phone, and many customers will discover the site from one. That made mobile a starting point for the public website, not a final check before launch.
The navigation, destination pages, contact form, payment page, media previews, and admin forms all had to work on a narrow screen. A desktop layout squeezed down to mobile would have made the most important tasks harder, especially uploading media and working through a custom itinerary.
The admin panel also needed to stay usable on a phone. Its controls are compact, but the editor still gets enough context to preview a destination and avoid losing unsaved work. A navigation guard warns before leaving a changed form and gives the option to save or discard it.
Letting the product decisions choose the stack
Once the workflows were clear, the technical shape became easier to choose.
The project lives in a Turborepo monorepo. The Next.js app is in apps/web, and shared packages contain the API, authentication, database, environment validation, and UI components.
The app uses Next.js 16, React 19, TypeScript, and Tailwind CSS. Motion, OGL, React Three Fiber, and Three.js handle the visual effects, including the globe and seasonal effects such as snowfall and northern lights.
For the application layer, I used tRPC with Zod. The browser and server share the same TypeScript workspace, so a change to an input shape is visible to its callers while working in the editor. The API has public procedures for public data, protected procedures for signed-in users, and admin procedures that also check the user's role.
The project is split into packages rather than putting everything in one large application:
1apps/web Next.js pages and route handlers2packages/api tRPC routers3packages/auth Better Auth4packages/db Prisma and Neon5packages/env validated environment variables6packages/ui shared React componentsThe data behind the conversations
Prisma connects the application to PostgreSQL on Neon. The database uses the Neon serverless adapter so the data layer fits the deployment model.
The core records are straightforward once the business is visible in them:
Destinationstores the public travel content, media, activities, globe settings, ordering, and publication state.Enquirystores the traveller's details, request, destination, dates, message, status, and notes.PaymentLinkstores a trip description, amount, currency, expiry, Razorpay identifiers, and payment state.- Auth and notification models store users, sessions, browser push subscriptions, and read state.
The policy pages were part of the same discovery process. Privacy, terms, cancellation, and FAQs are easy to postpone because they do not feel like the main product. They still need a clear place in the public site, and they affect the navigation and content structure from the start.
From enquiry to custom plan
An enquiry should not end as an email sitting in an inbox.
The public contact form creates an Enquiry. From the admin panel, the business can review it, add notes, change its status, and create a custom plan.
The plan editor pre-fills the traveller's destination, dates, and email. Once the dates are selected, the number of itinerary days is calculated and the matching day fields appear. Services such as accommodation, flights, transfers, meals, and concierge support can be selected with extra details.
When the plan is ready, the app renders an email with React Email and sends it through Resend. The operator does not have to build an HTML email or assemble a complicated request by hand. The structure exists because the workflow needed it.
Payments and notifications
Payment links have their own model and public route. An admin can create a link with the traveller's name, trip details, amount, currency, inclusions, travel dates, and expiry. Razorpay handles the payment flow, and the webhook updates the stored payment state after confirmation.
Enquiry notifications are separate records connected to the enquiry. Authenticated admins can receive browser push notifications, and each user has their own read state. That separation lets old notifications expire without deleting the enquiry they refer to.
What took the most time
The hardest part was translating a business conversation into decisions that could survive contact with a real user.
“Make it feel luxurious” had to become a theme, a font, a layout, and a set of media choices.
“I need an admin panel” had to become forms that did not expose JSON, image URLs, or database concepts.
“Customers should be able to enquire” had to become a flow from a mobile form to an enquiry, then to a custom itinerary and an email.
The code matters, but it came after those conversations. The stack gave the product room to grow, but the product decisions told the stack what to do.
That is what I will remember about Aluxbound. The visible result is a travel website. The less visible work was deciding what the website and the admin panel should mean to someone who would use them every day.
