Extend Bridge UI TypeScript types for custom tokens and provider config.
Bridge UI uses empty override interfaces and declaration merging so you can add custom prop values and tighten provider config types without forking the library.
There are two layers:
- Prop overrides — ButtonColorOverrides, ButtonSizeOverrides, … on the framework package. They enlarge unions like color and size.
- Config overrides — ButtonConfigOverrides, … on @bridge-ui/core/Config. Framework packages ship augments.d.ts that fills classes and defaultProps with framework-specific types.
Load framework augments
augments.d.ts wires @bridge-ui/core/Config interfaces to React or Vue component types. Include it once in your project so components.Button.classes is typed as ButtonClasses.
Create src/bridge-ui.d.ts (or similar):
/// <reference path="../node_modules/@bridge-ui/vue/dist/augments.d.ts" />
export {};
/// <reference path="../node_modules/@bridge-ui/react/dist/augments.d.ts" />
export {};
Adjust the relative path so it points at node_modules/@bridge-ui/<framework>/dist/augments.d.ts, and ensure that file is part of your TypeScript include.
After that, provider config gets precise classes and defaultProps keys for each component.
Prop overrides for custom tokens
When you register a new token with tokens, augment the matching override interface so TypeScript accepts the new value:
import "@bridge-ui/vue";
declare module "@bridge-ui/vue" {
interface ButtonColorOverrides {
brand: true;
}
interface ButtonRoundedOverrides {
pill: true;
}
}
export {};
import "@bridge-ui/react";
declare module "@bridge-ui/react" {
interface ButtonColorOverrides {
brand: true;
}
interface ButtonRoundedOverrides {
pill: true;
}
}
export {};
MergeProps turns keys on the override interface into allowed prop values, so color="brand" and rounded="pill" type-check.
Other common interfaces: ButtonSizeOverrides, ButtonVariantOverrides, ButtonDensityOverrides, and the same pattern on Avatar, Badge, Alert, FormField, and so on.
Extending config overrides further
You can deepen config typing the same way the framework does—by merging into @bridge-ui/core/Config:
declare module "@bridge-ui/core/Config" {
interface ButtonConfigOverrides {
// example: lock defaultProps.color to your brand token only
defaultProps: {
color?: "brand" | "primary";
};
}
}
export {};
Overwrite replaces matching keys on the base config, so only ship the fields you intend to redefine.
Custom global values
Packages can add their own keys to global by augmenting BridgeUIGlobal:
import "@bridge-ui/core/Config";
declare module "@bridge-ui/core/Config" {
interface BridgeUIGlobal {
editor?: { format: (value: string) => string };
}
}
export {};
app.use(
createBridgeUI({
global: { editor: { format: (value) => value.trim() } },
}),
);
<BridgeUIProvider global={{ editor: { format: (value) => value.trim() } }}>
<App />
</BridgeUIProvider>
Nested providers and setGlobal merge every key by the same rule:
- Plain data (breakpoints, formDefaults, custom settings objects) is deep-merged.
- Functions, class instances, and objects with a function member (adapters) are replaced — the last defined value wins, and undefined keeps the previous one.
Only the top-level members of a value are checked. An object whose functions sit deeper ({ hooks: { onSave } }) is deep-merged.
Custom components
Components shipped outside Bridge can use the registry too. Augment BridgeUIComponentsRegistry so components.<Name> is typed:
import "@bridge-ui/core/Config";
declare module "@bridge-ui/core/Config" {
interface BridgeUIComponentsRegistry {
Editor: Partial<{
classes: { root?: string };
defaultProps: Partial<{ size: "sm" | "md" | "lg" }>;
}>;
}
}
export {};
Then read the entry with useBridgeUIComponent from the framework Utils subpath. It returns merged props plus the raw registry entries entry and chromeEntry (classes, tokens):
import { useBridgeUIComponent } from "@bridge-ui/vue/Utils";
type EditorProps = { size?: "sm" | "md" | "lg"; variant?: string };
export function useEditor(props: EditorProps) {
const { merged, entry, chromeEntry } = useBridgeUIComponent<EditorProps>({
chrome: "FormField",
props: () => props,
componentName: "Editor",
libDefaults: { size: "md" },
});
return { merged, entry, chromeEntry };
}
import { useBridgeUIComponent } from "@bridge-ui/react/Utils";
type EditorProps = { size?: "sm" | "md" | "lg"; variant?: string };
export function useEditor(props: EditorProps) {
const { merged, entry, chromeEntry } = useBridgeUIComponent<EditorProps>({
props,
chrome: "FormField",
componentName: "Editor",
libDefaults: { size: "md" },
});
return { merged, entry, chromeEntry };
}
Pass chrome to inherit a shared entry (FormField, FormControl, BaseField, TimePanel). Its defaultProps apply below the component’s own entry, and form chrome (FormField, FormControl, BaseField) also applies global.formDefaults. Colorable components also receive global.defaultColor.
app.use(
createBridgeUI({
components: {
Editor: { defaultProps: { size: "sm" } },
FormField: { defaultProps: { variant: "filled" } },
},
}),
);
<BridgeUIProvider
components={{
Editor: { defaultProps: { size: "sm" } },
FormField: { defaultProps: { variant: "filled" } },
}}
>
<App />
</BridgeUIProvider>
Checklist
| Goal | What to do |
|---|---|
| Typed provider classes | Reference framework augments.d.ts |
| Accept color="brand" | Register runtime tokens + ButtonColorOverrides |
| Stricter defaultProps | Augment *ConfigOverrides on @bridge-ui/core/Config |
| Custom global key | Augment BridgeUIGlobal on @bridge-ui/core/Config |
| Register your own component | Augment BridgeUIComponentsRegistry + useBridgeUIComponent |
| Remap teal → brand hues | Prefer theme colors (no TS overrides) |
Related
- Tokens — runtime token registration
- Default props — app-wide defaults
- Classes — slot class maps