·5 min read

Rebranding a Live Product to Melio: 250 String Replacements, Zero Downtime

rebranduxmobile-firstmeal-plan-aimelioi18n|
LinkedIn

Rebranding a Live Product to Melio: 250 String Replacements, Zero Downtime

The product had three names. Depending on which package you were looking at, it was "Meal Plan AI", "NutriPlan", or "Meal Plan Agent". The public site said one thing, the dashboard said another, and the footer said a third. That's not a brand — that's a mess.

So I renamed everything to Melio and fixed the mobile experience in the same PR. Here's how both halves went.

Why "Melio"

The domain meal-plan.app is SEO gold — exact match for the category — and it stays. But "Meal Plan AI" as a brand name is a category description, not an identity. No one says "I use Meal Plan AI" the way they say "I use Notion" or "I use Linear".

I needed something short, globally pronounceable, and emotionally connected to food and health without being literal about it. After evaluating 25+ candidates — Nouri (pronunciation issues), Sated (taken by a keto brand), Vivaro (old Opel model), Kalo (too tech-y) — I landed on Melio. From the Latin "melius", meaning "better". Two syllables, reads the same in every language, and when someone searches "Melio meal plan" there's zero confusion with Melio Payments (a B2B fintech company in a completely different space).

The positioning shift matters too. The product isn't just an app — it's an AI nutrition agent. The tagline became "Your nutrition agent", aligning with how the industry is moving from tools to agents.

The Scope: 250 Replacements Across 80+ Files

This is a monorepo with two Next.js frontends (web-public and web-agent), a NestJS server, a Python AI service, and shared packages for i18n and UI components. The brand name lived everywhere:

  • 65 i18n locale files for the public site (diets, scenarios, features, resources, legal pages)
  • 3 web-agent locale files that each used a different name
  • 5 components with hardcoded strings (Navbar, Footer, Header)
  • Config files — site.ts, web manifest, llms.txt
  • Python service — FastAPI title and docstrings
The approach followed a 10-phase plan. Phases 1-2 established the foundation: update brand.json, seo.json, footer.json, and site.ts so the core config was correct. Phase 3 fixed hardcoded component strings. Phase 4 was the bulk operation — a targeted find-and-replace across all 65 page JSON files, with manual review for edge cases like the "best meal planning apps" comparison article that references the product alongside competitors.

Phases 5-6 consolidated the web-agent's three names into one and repeated everything for Polish and Ukrainian locales. "Melio" stays untranslated — it's a proper noun in all languages. Phases 7-9 covered llms.txt, legal pages, and the Python service.

What I deliberately didn't change: package names (@meal-plan/common), Docker image tags, the domain itself, and the Stripe subscription SKU. Internal infrastructure names don't face users, and changing them would break imports and deployment pipelines for zero user benefit.

Mobile-First Audit: 27 Issues in One Pass

The rebrand PR was also the right moment to do a full mobile audit — I was already touching every page. What I found was worse than expected.

Blank Pages

Two critical pages — nutrition profiles view and plans view — rendered completely blank. They were missing a QueryClientProvider wrapper, so React Query hooks threw silently and the component tree never mounted. This had been live for an unknown amount of time. Nobody reported it because users could still navigate via the list views.

Layout Breakpoints Were Wrong

The dashboard and wizard used md:grid-cols-2 for their grid layouts. With the sidebar open, md (768px) minus the sidebar width left cards squeezed into unusable proportions. Changed everything to lg:grid-cols-2 so the two-column layout only kicks in when there's actually room for it.

Data Tables on Mobile

Every data table (plans, participants, nutrition profiles, generated plans) overflowed horizontally on mobile with no scroll indicator. Added overflow-x-auto to all table containers and hid less-important columns (Gender, Created date, Status, Source Plan) on small screens. The information is still accessible in the detail view — the table just needs to show enough to identify and tap a row.

AppShell Component

Auth pages (login, register) were rendering with the sidebar and navbar visible, which made no sense. Created an AppShell component that conditionally hides navigation chrome on auth routes. Simple pattern: check the pathname, render children with or without the shell.

Dark Theme Pricing Cards

PricingCards had hardcoded hex colors for tier badges and feature highlights. They looked fine in light mode and terrible in dark. Converted everything to CSS variable tokens that respect the theme. Same pattern I used in the day detail UX overhaul — CSS variables in globals.css, components reference variables, dark mode overrides live in one place.

Other Fixes

  • Mobile sidebar drawer now has a backdrop overlay (tap outside to close)
  • Pagination showed "0 of 0" on empty tables instead of hiding
  • Login/register divider text ("or continue with") was wrapping on narrow screens
  • Profile page email/badge row overflowed — added flex-wrap
  • Added the wizard summary step (step 4) that was configured but not rendered

What I Learned

  • Three names is worse than a bad name. Inconsistency erodes trust faster than a mediocre brand. Pick one name and enforce it everywhere.
  • Rebrand through i18n, not find-and-replace. Having all user-facing strings in locale JSON files made this a config change, not a code change. The components never knew the brand name changed.
  • Mobile audits should happen during large refactors. You're already touching every page. The marginal cost of checking responsive behavior while you're there is near zero.
  • Blank pages can hide in plain sight. If the app has multiple navigation paths to the same data, a broken path might go unreported for weeks. QueryClientProvider missing was a silent killer.
The PR touched 97 files: +2,312 lines, -927 lines. Most of that is i18n string replacements and the new research docs. The actual code changes — AppShell, table fixes, theme tokens — were surprisingly small for the impact they had.