Skip to main content

003 - Dynamic Config

Your plugin was generated for the frontend system, which is config-first. That means you can control frontend components through app-config.yaml without changing any code.

Disabling an extension

Every extension in the frontend system can be toggled on or off through configuration. To disable the todo page entirely, add the following to your app-config.yaml:

app-config.yaml
app:
extensions:
- 'page:todo': false

Start the app and try navigating to /todo — you get a "page not found" response. Remove the line (or set it to true) to bring it back.

Configuring an extension

Every extension blueprint supports its own set of configuration options that adopters can set through app-config.yaml. PageBlueprint supports path and title out of the box. To change the page title, add the following:

app-config.yaml
app:
extensions:
- page:todo:
config:
title: My Custom Todo List

Restart the app and you should see "My Custom Todo List" as the page title. No code changes needed — the PageBlueprint reads the title config and passes it to the page header automatically.

Adding custom configuration

When the built-in config options are not enough, you can define your own config schema. Values are validated automatically and passed to your extension factory so that your components never need to read raw configuration directly.

For example, let's add a configurable subtitle. In plugin.tsx, switch from PageBlueprint.make to PageBlueprint.makeWithOverrides and declare a config schema:

export const page = PageBlueprint.makeWithOverrides({
config: {
schema: {
subtitle: z => z.string().optional(),
},
},
factory(origFactory, { config }) {
return origFactory({
path: '/todo',
routeRef: rootRouteRef,
loader: () =>
import('./components/TodoPage').then(m => (
<m.TodoPage subtitle={config.subtitle} />
)),
});
},
});

Then update TodoPage to accept the new prop and render it:

export function TodoPage({ subtitle }: { subtitle?: string }) {
// ... existing component code
return (
<Container>
{subtitle && <Typography variant="subtitle1">{subtitle}</Typography>}
{/* rest of the page */}
</Container>
);
}

Adopters can now set the subtitle in their app-config.yaml:

app-config.yaml
app:
extensions:
- page:todo:
config:
subtitle: Things to get done today

The value flows from configuration, through the schema validation, into the factory function, and finally into the component as a prop — no configApiRef needed.

Why does this work?

The frontend system treats configuration as a first-class concept. Each extension is registered with the app under a unique ID (for example, page:todo). The app reads the app.extensions section of the configuration to decide which extensions to enable, disable, or reconfigure.

Extension blueprints declare a config.schema using Zod validators. When the app starts, the framework parses and validates the configuration against the schema, then passes the result to the extension's factory function. This means your components receive typed, validated values instead of reading raw configuration strings at runtime.

This config-first approach means that adopters of your plugin can customize its behavior without forking the code — they only need to adjust their configuration files.