# categories manifest member

> The categories member is an array of lowercase strings naming the store sections a web app belongs in; browsers parse it but render nothing from it.

`categories` is an array of strings naming the store sections a web app belongs in, such as
`"productivity"` or `"games"`. The W3C Application Information registry defines it as a hint to
catalogs and stores; a browser engine parses the member but changes nothing about how the app
installs, launches, or renders.

Because nothing in the engine consumes it, browser-compat-data lists no version for any
browser (BCD `html.manifest.categories`). Chromium's manifest parser (`manifest_parser.cc`)
accepts the member and lowercases each string; Firefox 157 and Safari 27 parse and discard it.
The consumers are distribution surfaces: store packagers such as PWABuilder read the array
when they generate listing metadata for the Microsoft Store or a Play Store wrapper.

## Member

- **Type**: array of strings. The registry publishes a set of known values: `business`,
  `education`, `entertainment`, `finance`, `fitness`, `food`, `games`, `government`, `health`,
  `kids`, `lifestyle`, `magazines`, `medical`, `music`, `navigation`, `news`,
  `personalization`, `photo`, `politics`, `productivity`, `security`, `shopping`, `social`,
  `sports`, `travel`, `utilities`, and `weather`. Other strings are legal; stores map them to
  their own taxonomy or ignore them.
- **Default**: an empty array. A store that finds no usable category classifies the app
  itself.
- **Example value**: `["food", "health", "lifestyle"]`.

Values are compared case-insensitively: the registry asks authors to write lowercase and the
Chromium parser lowercases every entry before storing it. A value that is not an array is
dropped with the parser's generic type message, `property 'categories' ignored, type array
expected.`, and the rest of the manifest is unaffected.

The member is advisory in both directions. A store is free to list the app under a section the
manifest did not name, and a category the manifest names is not shown anywhere in the installed
app or in the browser's install dialog.

:::observed
Chrome 155 on macOS 26 (English UI), DevTools > Application > Manifest: the panel has no row
for `categories`. Its **Identity** section lists only Name, Short name, Description, Computed
App Id, and its **Presentation** section lists Start URL, Theme color, Background color,
Orientation, and Display. The only way the panel reacts to the member is under **Errors and
warnings**, where `"categories": "games"` (a string instead of an array) produces
`property 'categories' ignored, type array expected.`
:::

## Examples

The member is a declaration with no runtime surface, so the examples cover how to write it and
how to check what shipped.

### Declaring several store sections for one app

A meal-planning app fits more than one section. Listing three categories lets a store that
supports multi-category listings surface it in each; a store that supports one category takes
the first it recognizes.

```json
{
  "name": "Meal Planner",
  "short_name": "Meals",
  "start_url": "/",
  "display": "standalone",
  "categories": ["food", "health", "lifestyle"]
}
```

Keep the first entry the one the app most belongs to: a packager that has to pick a single
primary category has nothing else to go on.

### Validating the shipped value in a build step

There is no API that reports whether a browser or a store acted on the member, so the useful
check is a build-time or test-time assertion against the manifest you deploy. The function
normalizes the array the way the Chromium parser does and flags values outside the registry's
known list.

```js
const KNOWN = new Set([
  'business', 'education', 'entertainment', 'finance', 'fitness', 'food', 'games',
  'government', 'health', 'kids', 'lifestyle', 'magazines', 'medical', 'music',
  'navigation', 'news', 'personalization', 'photo', 'politics', 'productivity',
  'security', 'shopping', 'social', 'sports', 'travel', 'utilities', 'weather',
]);

export function checkCategories(manifest) {
  const raw = manifest.categories;
  if (raw === undefined) return { declared: [], unknown: [] }; // optional member
  if (!Array.isArray(raw)) throw new Error("categories must be an array of strings");
  const declared = raw.map((c) => String(c).toLowerCase());
  const unknown = declared.filter((c) => !KNOWN.has(c));
  return { declared, unknown };
}
```

An absent member is a valid manifest, so the function returns an empty list rather than
failing; an unknown value is reported, not rejected, because stores accept free-form strings.

### Reading the value from the page's own manifest

For a settings or "about" page that mirrors store metadata, fetch the manifest the document
links to and read the array. The fallback branch handles pages that ship no manifest link.

```js
async function declaredCategories() {
  const link = document.querySelector('link[rel="manifest"]');
  if (!link) return []; // no manifest linked from this document
  const manifest = await fetch(link.href).then((r) => r.json());
  return Array.isArray(manifest.categories) ? manifest.categories : [];
}
```

This confirms what the site declared, nothing more: it cannot tell whether a store listed the
app under those sections.

## See also

- [description manifest member](/reference/manifest/description/)
- [screenshots manifest member](/reference/manifest/screenshots/)
- [Distribute a PWA via app stores](/guides/app-store-distribution/)
- [Web App Manifest - Application Information: categories member](https://w3c.github.io/manifest-app-info/#categories-member) (w3c.github.io)
- [categories](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/categories) (developer.mozilla.org)