Skip to the solution
EasyFrontend8 min read

How do metadata files like favicon.ico, icon, opengraph-image and manifest work in Next.js?

998 views

The problem

Question presented to candidate: "Our site needs a favicon, a home-screen icon, a preview image when links are shared on social media, and a web app manifest. How do you add these in a Next.js App Router project, and what does Next.js do with the files?"

What a strong answer should cover:

  • Metadata files are special file names in app/: favicon.ico (root only), icon and apple-icon, opengraph-image and twitter-image (with optional .alt.txt), manifest.json/.webmanifest or manifest.ts, plus sitemap and robots.
  • Each can be a static file or code (.tsx/.ts) that generates it, for example with ImageResponse from next/og.
  • Next.js serves each file at a URL and writes the matching <head> tags automatically (rel icon, apple-touch-icon, og:image with type, width, height and alt, rel manifest), adding a content hash to the URL.
  • Files apply to their route segment and below; a deeper opengraph-image overrides a parent one.
  • Generated files are special Route Handlers, prerendered and cached by default unless they use request data; set metadataBase so image URLs are absolute.

Clarifying questions expected:

  • "Does each page need its own share image, or is one per site enough?" β€” static file versus generated per segment.
  • "Do we want the site installable as a PWA?" β€” then the manifest needs proper icons.

Code / implementation expected: Yes β€” a file layout and one generated image or manifest.

  • nextjs
  • metadata
  • seo
  • file-conventions

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

8 min read Β· 9 sections

Read the code

Static metadata files in app/
app/
β”œβ”€β”€ favicon.ico                  # root only   -> <link rel="icon" href="/favicon.ico?...">
β”œβ”€β”€ icon.png                     # any segment -> <link rel="icon" sizes="32x32" type="image/png">
β”œβ”€β”€ apple-icon.png               #             -> <link rel="apple-touch-icon" sizes="180x180">
β”œβ”€β”€ opengraph-image.tsx          # generated   -> og:image + twitter:image tags for "/"
β”œβ”€β”€ manifest.ts                  # root only   -> <link rel="manifest" href="/manifest.webmanifest">
β”œβ”€β”€ layout.tsx                   # set metadataBase here so image URLs are absolute
└── about/
    β”œβ”€β”€ page.tsx
    β”œβ”€β”€ opengraph-image.png      # overrides the root image for /about
    └── opengraph-image.alt.txt  # -> og:image:alt (save it without a trailing newline)
Generated Open Graph image and web manifest
// app/opengraph-image.tsx β€” generated at build time unless it uses request data
import { ImageResponse } from "next/og";

export const alt = "Acme β€” tools for builders";
export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export default function Image() {
  return new ImageResponse(
    (
      <div
        style={{
          width: "100%",
          height: "100%",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          fontSize: 96,
          background: "#0b1220",
          color: "white",
        }}
      >
        Acme
      </div>
    ),
    { ...size }
  );
}

// app/manifest.ts β€” served as /manifest.webmanifest
import type { MetadataRoute } from "next";

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: "Acme",
    short_name: "Acme",
    start_url: "/",
    display: "standalone",
    background_color: "#ffffff",
    theme_color: "#1e5ac8",
    icons: [{ src: "/icon.png", sizes: "32x32", type: "image/png" }],
  };
}

// app/layout.tsx (excerpt) β€” makes og:image an absolute URL on your real domain
import type { Metadata } from "next";
export const metadata: Metadata = {
  metadataBase: new URL("https://acme.example"),
  title: "Acme",
};

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 4 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextEasy

How does client-side navigation work in Next.js (next/link and useRouter)?

Continue