App Landing Hero with Phone Mockup

A hero for a mobile app landing page. The left side has a short label, a bold headline, one sentence and two download buttons, one filled and one outlined, each with a small device icon and a two-line label. Under them is a row of initial avatars with a line of social proof. The right side is a phone drawn with rounded shapes: a status bar, tonight's recipe card, a week strip, a shopping list and a tab bar. Two small floating cards sit beside it on larger screens. On phones everything is centred, the buttons go full width and the mockup sits below the text.

Code

const stores = [
  { platform: "iPhone", dark: true },
  { platform: "Android", dark: false },
];

const week = [
  { day: "M", done: true },
  { day: "T", done: true },
  { day: "W", done: true },
  { day: "T", today: true },
  { day: "F" },
  { day: "S" },
  { day: "S" },
];

const shopping = [
  { item: "Orzo, 500 g", got: true },
  { item: "Chickpeas, 2 tins", got: true },
  { item: "Lemons, 3", got: false },
];

const cooks = [
  { initials: "AO", colour: "bg-amber-300" },
  { initials: "LK", colour: "bg-sky-300" },
  { initials: "RM", colour: "bg-emerald-300" },
];

export default function AppLandingHeroWithPhoneMockup() {
  const ring = "focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-600 focus-visible:ring-offset-2 focus-visible:ring-offset-rose-50";
  return (
    <section className="overflow-hidden bg-rose-50 px-6 py-16 text-slate-900 sm:py-20">
      <div className="mx-auto grid max-w-6xl items-center gap-14 lg:grid-cols-2 lg:gap-8">
        <div className="text-center lg:text-left">
          <p className="text-sm font-semibold uppercase tracking-widest text-rose-700">Supper, the meal planning app</p>
          <h1 className="mt-4 text-4xl font-extrabold tracking-tight sm:text-6xl">A week of dinners, planned in five minutes.</h1>
          <p className="mx-auto mt-5 max-w-md text-lg leading-relaxed text-slate-700 lg:mx-0">
            Pick the meals you fancy and Supper writes one tidy shopping list, sorted by aisle. No more staring into the fridge at six o'clock.
          </p>
          <div className="mt-8 flex flex-col items-stretch gap-3 sm:flex-row sm:justify-center lg:justify-start">
            {stores.map((s) => (
              <a key={s.platform} href="#" className={`inline-flex items-center justify-center gap-3 rounded-2xl px-6 py-3 transition sm:justify-start ${ring} ${s.dark ? "bg-slate-900 text-white hover:bg-slate-700" : "border-2 border-slate-900 text-slate-900 hover:bg-white"}`}>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="h-7 w-7 flex-none"><rect x="6.5" y="2.5" width="11" height="19" rx="2.5" /><path d="M12 7.5v6m0 0-2.5-2.5M12 13.5l2.5-2.5M10 17.5h4" /></svg>
                <span className="text-left leading-tight">
                  <span className="block text-xs font-medium">Get the app for</span>
                  <span className="block text-lg font-bold">{s.platform}</span>
                </span>
              </a>
            ))}
          </div>
          <div className="mt-8 flex items-center justify-center gap-3 lg:justify-start">
            <div className="flex -space-x-2" aria-hidden="true">
              {cooks.map((c) => (
                <span key={c.initials} className={`flex h-9 w-9 items-center justify-center rounded-full border-2 border-rose-50 text-xs font-bold text-slate-900 ${c.colour}`}>{c.initials}</span>
              ))}
            </div>
            <p className="text-left text-sm text-slate-700"><span className="font-semibold text-slate-900">60,000 home cooks</span> plan with Supper.<br />Free to download.</p>
          </div>
        </div>

        <div aria-hidden="true" className="relative mx-auto w-full max-w-md">
          <div className="absolute left-1/2 top-1/2 h-80 w-80 -translate-x-1/2 -translate-y-1/2 rounded-full bg-rose-200 sm:h-[30rem] sm:w-[30rem]" />
          <div className="absolute -left-10 bottom-24 z-10 hidden w-40 rounded-2xl bg-white p-4 shadow-xl shadow-rose-200 sm:block">
            <p className="text-xs font-medium text-slate-500">This week</p>
            <p className="mt-1 text-sm font-bold">5 dinners sorted</p>
            <div className="mt-3 h-1.5 rounded-full bg-rose-100"><div className="h-1.5 w-5/6 rounded-full bg-rose-600" /></div>
          </div>
          <div className="absolute -right-12 top-20 z-10 hidden items-center gap-3 rounded-2xl bg-slate-900 p-4 text-white shadow-xl shadow-rose-300 sm:flex">
            <span className="flex h-9 w-9 items-center justify-center rounded-full bg-rose-600">
              <svg viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4"><path fillRule="evenodd" d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z" clipRule="evenodd" /></svg>
            </span>
            <div><p className="text-sm font-bold">List ready</p><p className="text-xs text-slate-300">18 items, 4 aisles</p></div>
          </div>

          <div className="relative mx-auto w-64 rounded-[2.75rem] bg-slate-900 p-2.5 shadow-2xl shadow-rose-300">
            <div className="overflow-hidden rounded-[2.25rem] bg-white">
              <div className="relative flex items-center justify-between px-6 pb-1 pt-3 text-xs font-semibold">
                <span>9:41</span>
                <span className="absolute left-1/2 top-2 h-5 w-20 -translate-x-1/2 rounded-full bg-slate-900" />
                <span className="flex items-center gap-1"><span className="h-2 w-3 rounded-sm bg-slate-900" /><span className="h-2.5 w-5 rounded-sm border border-slate-900 p-px"><span className="block h-full w-3/4 rounded-sm bg-slate-900" /></span></span>
              </div>
              <div className="px-4 pb-3 pt-4">
                <p className="text-xs font-medium text-slate-500">Thursday</p>
                <p className="text-lg font-extrabold tracking-tight">Tonight's dinner</p>
                <div className="relative mt-3 h-32 overflow-hidden rounded-2xl bg-gradient-to-br from-amber-200 to-rose-300">
                  <span className="absolute -bottom-10 -right-6 h-36 w-36 rounded-full bg-white/70" />
                  <span className="absolute -bottom-6 -right-2 h-28 w-28 rounded-full bg-amber-100" />
                  <span className="absolute bottom-3 right-7 h-9 w-9 rounded-full bg-yellow-300" />
                  <span className="absolute bottom-10 right-3 h-6 w-6 rounded-full bg-emerald-400" />
                  <span className="absolute bottom-1 right-16 h-5 w-5 rounded-full bg-rose-400" />
                  <span className="absolute left-3 top-3 rounded-full bg-white px-2.5 py-1 text-xs font-bold">25 min</span>
                </div>
                <p className="mt-3 text-sm font-bold">Lemon chickpea orzo</p>
                <p className="text-xs text-slate-500">Serves 4, one pan</p>
                <div className="mt-4 flex justify-between">
                  {week.map((d, i) => (
                    <span key={i} className={`flex h-9 w-7 flex-col items-center justify-center gap-1 rounded-full text-xs font-bold ${d.today ? "bg-rose-600 text-white" : "text-slate-600"}`}>
                      {d.day}
                      <span className={`h-1 w-1 rounded-full ${d.today ? "bg-white" : d.done ? "bg-rose-600" : "bg-slate-200"}`} />
                    </span>
                  ))}
                </div>
                <div className="mt-4 rounded-2xl bg-slate-50 p-3">
                  <div className="flex items-center justify-between text-xs"><span className="font-bold">Shopping list</span><span className="font-medium text-slate-500">2 of 8</span></div>
                  <ul className="mt-2 space-y-2 text-xs">
                    {shopping.map((s) => (
                      <li key={s.item} className="flex items-center gap-2">
                        <span className={`flex h-4 w-4 flex-none items-center justify-center rounded-full ${s.got ? "bg-rose-600 text-white" : "border-2 border-slate-300"}`}>
                          {s.got && <svg viewBox="0 0 20 20" fill="currentColor" className="h-3 w-3"><path fillRule="evenodd" d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z" clipRule="evenodd" /></svg>}
                        </span>
                        <span className={s.got ? "text-slate-400 line-through" : "font-medium text-slate-800"}>{s.item}</span>
                      </li>
                    ))}
                  </ul>
                </div>
              </div>
              <div className="flex items-center justify-around border-t border-slate-100 px-4 pb-4 pt-3">
                <span className="h-5 w-5 rounded-md bg-rose-600" />
                <span className="h-5 w-5 rounded-full border-2 border-slate-300" />
                <span className="h-5 w-5 rounded-md border-2 border-slate-300" />
                <span className="h-5 w-5 rounded-full border-2 border-slate-300" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

Install with one command: npx shadcn@latest add https://we.inc/r/app-landing-hero-with-phone-mockup.json

When to use it

How to customize it

Accessibility

Questions

Can I use the official store badges instead?

Yes. Each store publishes its own badge artwork and rules for size and spacing. Replace the contents of each link with the badge image and remove the background classes. The generic buttons here are useful before launch or when you want a matching pair.

How is the phone drawn without an image?

It is a dark rounded rectangle with padding for the bezel, and a white rounded rectangle inside for the screen. The notch is a small pill positioned at the top. Everything on the screen is ordinary text and shapes.

How do I send each visitor to the right store?

Give each button its own store address. If you would rather show one button, read the device type in a small script and swap the link, but keep both visible on desktop where people may scan or share.

Should the phone come before the text on mobile?

Usually not. The headline tells people what the app does, and the mockup supports it. If your app is very visual you can add `order-first lg:order-none` to the mockup column.

Will the mockup look sharp on high resolution screens?

Yes. It is built from HTML and CSS shapes, so it stays crisp at any pixel density and adds almost nothing to the page weight.

Related: Split Hero with Email Capture, Dark Hero with Gradient Glow and Product Window, Centered Hero with Two Buttons and Logo Row, Button Set: Primary, Secondary, Ghost, Icon and Loading.

More hero sections or all free UI components. Start for free to have a full site built around it.

More in Hero Sections

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap