Groundfloor Docs
Shell apps

Manifest and routes

Describe your Shell app's pages, navigation and colours in groundfloor.manifest.json, and link between pages.

groundfloor.manifest.json tells the Shell about your app: its name, its pages and the order they appear in the navigation, and its colours. gf apps publish sends it along with each release, so the file in your project is the one that counts.

Example

groundfloor.manifest.json
{
  "version": "1.0",
  "appId": "my-first-app",
  "name": "My First App",
  "theme": {
    "primaryColor": "#2563EB",
    "accentColor": "#7C3AED",
    "mode": "light"
  },
  "routes": [
    { "path": "home", "title": "Home", "icon": "Home" },
    { "path": "records", "title": "Records", "icon": "FileText" },
    { "path": "records/:id", "title": "Record", "hidden": true }
  ]
}
FieldRequiredMeaning
versionYesThe manifest format. Use "1.0"
appIdYesYour app's slug. Must equal APP_ID in src/appIdentity.ts
nameYesThe name shown in the Shell, up to 120 characters
themeYesYour colours. See below
routesYesYour pages. See below
description, iconNoA short description, and an icon for the app

remoteUrl is filled in when you publish; leave it empty.

Routes

Each route is one page of your app, at /apps/<app-slug>/<path> on your Shell site.

FieldRequiredMeaning
pathYesThe page's path within your app, such as records or records/:id. No leading /
titleYesIts label in the navigation
iconNoA Lucide icon name, such as FileText
hiddenNotrue keeps the page out of the navigation but still reachable, e.g. a detail page opened from a list
visibilityNoauthenticated (the default) needs a signed-in user; public doesn't

Add a page in two places: a route here, and the matching page component in src/App.tsx.

Your app lives under /apps/<app-slug>/ on the Shell site, so never write absolute links such as to="/records": they leave your app. Use the starter kit's useAppPath:

import { Link } from "react-router-dom";
import { useAppPath } from "./lib/useAppPath";

export function RecordsLink() {
  return <Link to={useAppPath("records")}>All records</Link>;
}

Don't wrap your app in BrowserRouter either: the Shell already provides the router, and a second one crashes the app.

Name a detail page in the breadcrumb

The Shell builds the breadcrumb from the URL, so a page like records/:id shows the raw ID. Set a readable label once you know it:

import { setBreadcrumbLabel } from "./lib/shellBridge";

useEffect(() => {
  if (record?.name) setBreadcrumbLabel(record.name);
}, [record?.name]);

Theme

FieldRequiredMeaning
primaryColorYesYour brand colour, as hex, such as #2563EB
accentColorYesA second colour, as hex
modeNolight (the default) or dark
logoUrlNoYour logo, as an absolute https:// URL

The Shell uses these for the frame around your app. See Styling for your own pages.

Next steps

On this page