Choose your app's features
Pick the Arc CMS features your app has in src/custom/features.ts. A feature that is off has no menu item, no page, no settings tab and no Cloud Function. Its data stays, and turning it back on brings everything back.
Choose
-
Open
src/custom/features.ts. As shipped, it is empty, which means every feature is on except the installable app:export const CUSTOM_FEATURES: FeatureChoice = {}; -
List what the app does not have in
off, and the installable app inonif you want it:import type { FeatureChoice } from '../app/core/features/feature-registry'; export const CUSTOM_FEATURES: FeatureChoice = { on: ['pwa'], off: ['content', 'sms', 'payments'], }; -
Save. A running
npm run devrestarts by itself and checks the choice;npm run buildchecks it first too. -
Deploy all the functions once, so the functions of the features you turned off are removed from the project (see Cloud Functions follow the features).
The features
Everything not in this table is core and always on: sign-in with email and Google, users and profiles, the media manager, languages, the feedback button, notifications, the email engine, automations, banners, and the admin and member dashboards.
| Id | Default | Needs | What goes when it is off |
|---|---|---|---|
content | On | Content types, entries, authors, static pages (/p/), public content pages and publishing | |
search | On | Search settings, the admin search box and its results page, the public /search page and every search function | |
seo | On | Discoverability settings, the sitemap, RSS feeds, robots.txt, llms.txt and IndexNow | |
forms | On | audience | Signup forms, their pages and emails |
audience | On | Contacts, lists, contact tags and fields, app users and the App audience settings | |
email-marketing | On | audience | Broadcasts, drip campaigns and announcements |
sms | On | SMS settings, SMS logs, test SMS and phone sign-in | |
payments | On | Products, transactions, payment settings, pricing, checkout, credits, and the member's Payments, Account and Premium pages | |
data | On | The Data menu: import and export | |
contact | On | The Messages inbox, contact forms on published pages and their functions | |
pwa | Off | The installable app: manifest, service worker, install prompt and install stats | |
analytics | On | Google Analytics in the browser, the dashboard's analytics section and Analytics settings, and their functions. To keep it but wait for consent, see Analytics |
Each feature has its own page under All features, and Feature ids lists the ids alone.
When the build refuses a choice
npm run dev and npm run build resolve the choice before anything else, so a choice that cannot work never reaches the browser. They stop with a message that starts with src/custom/features.ts: and says what to change:
| You wrote | The message |
|---|---|
A name that is not a feature, such as off: ['payment'] | unknown feature "payment" in "off". Features: content, search, seo, ... |
| The same feature in both lists | Installable app (pwa) is in both "on" and "off". Keep it in one. |
A feature off while another one needs it, such as off: ['audience'] | Signup forms (forms) needs Audience (audience), which is off. Turn off forms as well, or keep audience. |
| Something other than a list | "off" must be a list, like off: ['payments'] |
A typo stops the build on purpose: it must never leave a feature on that you meant to turn off.
What turning a feature off does
In the browser
At the next build, or straight away with npm run dev:
- Its menu items, settings tabs, dashboard cards and member-area links go.
- Every one of its addresses shows the not-found page and keeps the address, bookmarks and old links included.
- Core screens adjust. For example, without
smsthe "Email + SMS" menu is "Email" and phone sign-in is not offered.
Cloud Functions follow the features
A feature's functions are exported from their own file in functions/src/features/, such as functions/src/features/payments.ts. A function that needs two features sits in a file named after both, such as functions/src/features/search+content.ts.
Before every functions build and every test run, scripts/arc-features.mjs reads src/custom/features.ts and writes two generated files in functions/src/: the list of features that are on, and the exports of every feature file whose features are all on. functions/src/all.ts re-exports that list, so a feature that is off is not in the build at all. Both files are ignored by git.
At the next deploy of all the functions, the functions that are no longer in the build are deleted from the project:
npm run deploylists them and asks first. Run from a script, it needs--yesto delete them and stops otherwise.npm run deploy:devandnpm run deploy:prodpass--force, so they delete them without asking.- A deploy of named functions never deletes anything (Deploy only what changed).
- Addresses the outside world holds stop working, such as a payment webhook registered with the payment provider.
See Deploy.
scripts/arc-features.mjs loads the TypeScript files directly, which needs Node 22.18 or later.
Data, rules and indexes
They stay as they are. Turn the feature back on, build and deploy, and everything is there again.
Make your own code follow the features
| Your code | How it checks a feature |
|---|---|
An admin menu item in src/custom/nav.ts | Set feature: 'payments' on the item (Change the admin menu) |
Routes in src/custom/routes.ts | Wrap them: ...whenOn('payments', [ ... ]), from src/app/core/features/feature-routes.ts (Add pages and routes) |
| A component or service | isOn('payments'), from src/app/core/features/features.ts |
A Cloud Function in functions/src/custom/ | isFeatureOn('payments'), from functions/src/feature-flags.ts |
The test suite always runs with every feature on, whatever src/custom/features.ts says, so an app that turns features off can still run all of it. A test about one particular choice mocks src/custom/features.ts itself, or, for a Cloud Function, functions/src/feature-flags.ts.