Published September 16, 202612 min read

Favicon Set and Dynamic OG Images

Outcome

A deployed website with a complete favicon and app-icon set, separate normal and maskable manifest icons, and a tested 1200x630 Open Graph image for every page template, including pages without artwork.

The n-g.be ice cubes and blue flame branching into a browser tab icon, a mobile app icon, and a social preview card.

Share this guide

Share this guide on LinkedIn

Summarize with AI

ChatGPTPerplexityClaude
Table of Contents

One Logo Has To Work In Several Places

A finished logo is not a finished icon system. Browser tabs, bookmarks, Apple home screens, Android launchers, installed web apps and shared links each read different files and crop them differently.

Do this after the logo is final and before the first deploy. Repeat it whenever the logo changes: an old icon survives in tabs, launchers and link previews long after the header has been updated.

Steps

Guide

  1. Freeze The Source Mark Before Generating Files

    Start from the icon-only SVG. Do not squeeze a wide wordmark into a square and call it a favicon.

    Confirm these inputs before generating anything:

    • approved SVG source
    • icon-only version
    • how the mark reads on light and dark tab bars
    • site name and short name
    • canonical site URL
    • theme and background colours

    At 16x16 pixels detail disappears. If the mark is unreadable at that size, simplify the source mark instead of sharpening a miniature wordmark.

  2. 2

    Generate The Pack From That One Source

    Read Guide

    Produce favicon.ico, an SVG icon, a 180x180 Apple touch icon, 192x192 and 512x512 Android/PWA PNGs, and a web app manifest. Keep the raster files even when SVG is the main icon: not every browser and launcher reads SVG.

    The Built by meN-G.BE Favicon Generator builds the browser, Apple, Android, manifest and 1200x630 social-image files from one logo. The favicon generator comparison covers five other routes and when each one fits better.

    A downloaded ZIP is an input, not proof that the site is configured. The checks at the end confirm that the public files and the rendered markup point to the intended assets.

  3. Declare Any And Maskable Icons Separately

    A normal any icon and a maskable icon solve different problems. The any icon can use transparency and a tight crop. The maskable icon needs an opaque background and padding, because Android cuts it into a circle, squircle or rounded square: web.dev puts the safe zone at a centred circle 80% of the icon's width.

    Avoid "purpose": "any maskable" on one file. Many generators still write it. It forces one image to do both jobs: without padding, Android crops the mark; with padding, every other surface shows a shrunken mark. The same set can look right on an iPhone, which uses the Apple touch icon, and wrong on Android, which reads the manifest.

    Check whether your pack contains a padded maskable file. If it does not, make one from the 512x512 icon in the Maskable.app editor and preview it against the common masks before accepting it.

    {
      "icons": [
        {
          "src": "/icons/icon-192.png",
          "sizes": "192x192",
          "type": "image/png",
          "purpose": "any"
        },
        {
          "src": "/icons/icon-512.png",
          "sizes": "512x512",
          "type": "image/png",
          "purpose": "any"
        },
        {
          "src": "/icons/icon-512-maskable.png",
          "sizes": "512x512",
          "type": "image/png",
          "purpose": "maskable"
        }
      ]
    }

    The field reference is on MDN.

  4. Install The Pack In Next.js

    Pick one route and do not mix them for the same file:

    • File conventions - favicon.ico at the root of app, plus icon.svg (or icon.png) and apple-icon.png in app. Next.js writes the head tags itself.
    • public folder plus metadata.icons - the files sit in public and the root layout lists them with their sizes and types. This is the usual route when a generator hands you a ready-made folder.

    For the manifest, either add app/manifest.ts returning a typed object, or keep the generator's site.webmanifest in public and point metadata.manifest at it. Raster icons need exact pixel sizes and a MIME type; an SVG icon takes sizes: "any".

    Either way, open the rendered page head and check rel, href, sizes and type. Filenames alone prove nothing. The conventions are in the Next.js app-icon documentation and the manifest documentation.

  5. Generate One 1200x630 OG Image Per Page Template

    Create opengraph-image.tsx beside the dynamic route's page.tsx. Load the record by slug, pick its artwork, and return an ImageResponse from next/og. Next.js adds the og:image tags for that route automatically.

    Two details break most first attempts. The renderer does not fetch a relative path such as /media/cover.png, so read local files and pass them as a data URL, or use an absolute URL. It does not decode WebP, so keep PNG or JPEG copies of the artwork for the social image.

    import { readFile } from "node:fs/promises";
    import path from "node:path";
    import { ImageResponse } from "next/og";
    
    export const size = { width: 1200, height: 630 };
    export const contentType = "image/png";
    
    const FALLBACK = "/og/placeholder.png";
    
    async function toDataUrl(publicPath: string) {
      const file = await readFile(path.join(process.cwd(), "public", publicPath));
      return `data:image/png;base64,${file.toString("base64")}`;
    }
    
    export default async function OpenGraphImage({
      params,
    }: {
      params: Promise<{ slug: string }>;
    }) {
      const { slug } = await params;
      const page = await getPage(slug);
      const image = await toDataUrl(page?.ogImagePng ?? FALLBACK);
    
      return new ImageResponse(
        (
          <div style={{ display: "flex", width: "100%", height: "100%" }}>
            <img src={image} alt="" width={1200} height={630} />
          </div>
        ),
        size,
      );
    }

    Keep the layout simple. ImageResponse supports flexbox and a subset of CSS, and every element with more than one child needs display: flex. Test the longest realistic title before polishing the normal case. Reference: Next.js opengraph-image and ImageResponse.

    On n-g.be, every article's OG image is a card built by this route: the article's banner on the left, its title, description, format and date on the right. The banners are WebP on the page, so the route reads a PNG copy of each one. An article without a banner gets a shared placeholder.

  6. Make The Fallback Part Of The Main Path

    A page without artwork must still return a valid social image. Keep one branded placeholder that works with any title and makes no route-specific assumptions.

    Treat a missing image and a missing page as two different cases. A known page without artwork uses the placeholder. An unknown slug should never produce a convincing preview for a page that does not exist; return a neutral brand card or follow the site's not-found behaviour.

    The fallback is done only when its public OG URL returns an image. A conditional that has never run is an untested branch.

  7. Check Files, Markup, Masks And Real Page URLs

    On the deployed site, open favicon.ico, the SVG icon, the Apple icon, the Android icons, the manifest and a few OG routes directly. Confirm a 200 response, the right content type and the intended dimensions.

    Then inspect the page head, open the manifest in the browser developer tools' Application panel, and confirm the maskable icon's safe area. Capture representative pages on desktop and mobile with the Built by meN-G.BE Website Screenshot Generator to review them together.

    Test at least one page with its own artwork, one on the fallback, and one with an unusually long title. Social platforms cache previews, so check the source OG URL before treating a stale shared card as a generation failure.

Be Aware

The favicon contains the full wordmark and becomes unreadable.

Return to the icon-only SVG and simplify detail for the smallest sizes instead of scaling the wordmark further.

The home-screen icon looks right on iPhone but is cropped or tiny on Android.

The manifest probably declares any maskable on one file. Split it into an any icon and a padded maskable icon, then preview the masks.

The OG route works in one place and returns a blank or broken image in another.

Check the image source: relative paths are not fetched and WebP is not decoded. Pass PNG or JPEG as a data URL or absolute URL, remove unsupported CSS, then open the fallback route directly.

A corrected social image still shows the old preview.

Open the source OG URL first. If it is correct, the platform is caching the old card; refresh it with the platform's own debugging tool instead of changing the generator again.

Favicon And OG Image Setup Brief

Copy / paste

Create and verify the complete favicon, app-icon, manifest, and dynamic Open Graph image setup for this Next.js website.

Read the project files before changing anything. Confirm these inputs:
- approved final logo and icon-only SVG
- site name and short name
- canonical site URL
- theme colour and background colour
- page templates and content lookup functions
- artwork field or image source for each template
- branded fallback image

Stop before implementation if the final logo, canonical URL, page lookup, or fallback decision is missing. Do not invent brand values or public routes.

Required icon outputs:
- favicon.ico
- SVG site icon
- 180x180 Apple touch icon
- 192x192 and 512x512 normal Android/PWA icons
- separate 512x512 maskable Android/PWA icon with safe padding (mark inside the central 80% circle)
- web app manifest with exact sizes, MIME types, and separate "any" and "maskable" purposes; never "any maskable" on one file

Next.js implementation:
- use either the App Router icon file conventions or public files listed in metadata.icons, not both for the same file
- expose the manifest through app/manifest.ts or a public site.webmanifest referenced by metadata.manifest
- create a 1200x630 opengraph-image.tsx for each page template that needs a unique preview
- use the page's own artwork when available and the approved placeholder when it is absent
- pass images to ImageResponse as PNG or JPEG, via a data URL or absolute URL; never a relative path or WebP
- keep ImageResponse layouts within its supported CSS subset

Acceptance checks:
- build and type checks pass
- every required asset returns 200 at its public URL
- the rendered head contains the correct icon, manifest, and Open Graph declarations
- normal and maskable icons are separate and the maskable preview keeps the mark visible
- a page with artwork returns the correct 1200x630 OG image
- a page without artwork returns the 1200x630 fallback
- the longest realistic title fits
- representative desktop and mobile pages have been visually checked

Report the files changed, URLs tested, checks run, and any decision that still needs human approval.

About the author

Nikita Goncharenko

Nikita Goncharenko

AI Fast Integrator

Nikita Goncharenko uses AI as a practical delivery layer for research, coding, documentation, content systems, and faster decisions.