Create Manifest file in Next JS from Headless WordPress (App Router)

Last Updated:
Next JS with WordPress to create a manifest - Next 13 App Router

Note: This article is for the App router. Not the Pages Router. See here for how to create a Manifest File dynamically with the Pages Router.

In Next.js, you have a few different options for create manifest files; the straightforward route is creating the file manually in the /public directory. While this methods is fine for for single-site builds, integrating a headless CMS like WordPress means you have dynamic data – so need to create the file dynamically.

These piece will walk through how to extend the WordPress Customizer with specific fields and leverage WPGraphQL to fetch this data into Next.js, presenting it as JSON within a manifest.json file.


  • Working knowledge of Next.js and WordPress
  • WPGraphQL installed on your WordPress site

Understanding Manifest Files

The manifest file is a JSON file. It instructs browsers on how your website should appear when installed on user devices as a Progressive Web App (PWA). It sets the website’s appearance and behaviour on mobile or desktop devices.

Is a Manifest File Needed?

Unless you are keen on allowing users to install your website as an app, enhancing your PWA experience, or foresee its necessity in the future, you might skip creating a manifest file.

How to name a Manifest file

While manifest.json is the prevalent choice, the specification suggests .webmanifest, spawning variations like site.webmanifest and app.webmanifest. These variants function identically.


We need to pull data from WordPress, inject it into a manifest file, and then serve it from /

The data to be sourced from WordPress is:

  • Site Name
  • Site Description
  • Theme Colour
  • Background Colour
  • Favicon Files

To make a more ‘full’ manifest file more data could be pulled from WordPress, but this works for our simplified example. If you are interested in seeing what can go into a manifest file take a look at this breakdown manifest members on Mozilla.

Looking at the list of requirements, not all of these are provided out of the box by WordPress. Theme and Background Colour will need to be added with custom functionality.

Adding Meta Fields to WordPress Customizer

The WordPress site name, description and site icon (favicon) information can all be added and edited within the Customizer section of WordPress. With this in mind it makes sense to add the theme colours meta fields into the WordPress Customizer as well. Keep everything under the same roof.

Add the below code to the WordPress functions.php file.

function headless_customizer_add_colour_picker( $wp_customize ){
    // Add Theme Colour Setting 
    $wp_customize->add_setting( 'headless_theme_colour', array(
        'default' => '#ffffff',
        'sanitize_callback' => 'sanitize_hex_colour',
    // Add Theme Colour Control
    $wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'my_headless_theme_colour', array(
        'label' => 'Theme Colour',
        'section' => 'title_tagline',
        'settings' => 'headless_theme_colour',
    // Add Theme Background Colour Setting 
    $wp_customize->add_setting( 'headless_theme_background_colour', array(
        'default' => '#ffffff',
        'sanitize_callback' => 'sanitize_hex_colour',
    // Add Theme Colour Background Control
    $wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'my_headless_theme_background_colour', array(
        'label' => 'Theme Background Colour',
        'section' => 'title_tagline',
        'settings' => 'headless_theme_background_colour',

add_action( 'customize_register', 'headless_customizer_add_color_picker' );

Now if you navigate to ‘Appearance -> Customize’ admin screen you will see these two new fields under “Site Identity”.

New WordPress theme color inputs

Now these custom fields need adding to the WP GraphQL endpoint. Add the following code into the WordPress functions.php file.

function add_headless_homepage_settings_query() {

		'type' => 'MediaItem',
			'description' => __( 'The icon set in the customizer', 'YOUR_WORDPRESS_DOMAIN' ),
			'resolve' => function() {

				$icon_id = get_option( 'site_icon' );

				if ( ! isset( $icon_id ) || ! absint( $icon_id ) ) {
					return null;

				$media_object = get_post( $icon_id );
				return new \WPGraphQL\Model\Post( $media_object );


		'type' => 'String',
			'description' => __( 'The theme color in the customizer', 'YOUR_WORDPRESS_DOMAIN' ),
			'resolve' => function() {
				$color = get_theme_mod( 'headless_theme_color', '#ffffff' );
				return $color;

		'type' => 'String',
			'description' => __( 'The theme background color in the customizer', 'YOUR_WORDPRESS_DOMAIN' ),
			'resolve' => function() {
				$color = get_theme_mod( 'headless_background_theme_color', '#ffffff' );
				return $color;

add_action( 'graphql_register_types', 'add_headless_homepage_settings_query' );

Creating the Dynamic Manifest File

At the root of the app folder create the file manifest.js.

This file will work in the same way as the page.js works. It allows us to pull data dynamically within the file.

Source the Data

export default async function manifest() {
  const res = await fetch("YOUR_WORDPRESS_WPGRAPHQL_URL", {
    body: JSON.stringify({
      query: `
        query { 
          siteIcon { 
            mediaDetails { 
              sizes { 
          allSettings { 
  const data = await res.json();

  return {
    name: data?.allSettings?.generalSettingsTitle || "",
    lang: "en-US",
    start_url: "/",
    background_color: data?.themeBackgroundColor || "#ffffff",
    theme_color: data?.themeColor || "#ffffff",
    display: "standalone",
      data?.siteIcon?.mediaDetails?.sizes?.map(({ sourceUrl, width }) => ({
        src: sourceUrl,
        sizes: `${width}x${width}`,
        type: "image/png",
      })) || [],

Adding the File to the <Head/>

If the manifest.js file exists, Next JS will automatically add it to the head of the document. There is nothing left to do!


Hopefully this helped you, and if you have any questions you can reach me at: @robertmars

Related Posts

Helpful Bits Straight Into Your Inbox

Subscribe to the newsletter for insights and helpful pieces on React, Gatsby, Next JS, Headless WordPress, and Jest testing.