| id | title | description |
|---|---|---|
adrs-adr015 |
ADR015: Types and naming for element and component options |
Architecture Decision Record (ADR) for the proper types and naming for element and component options |
Until now there hasn't been a clear standard for how to define options that are intended to provide JSX elements or components. This led to a mix of different patterns in public APIs, which this ADR aims to standardize.
We will use one of the following option property names and types when defining options that are intended to provide JSX elements or components:
This option is used when a simple synchronous JSX element is provided. It must only be used in areas where lazy-loading is not needed.
{
element: JSX.Element;
}This option is used when a simple synchronous component is provided. It must only be used in areas where lazy-loading is not needed.
{
component: (props: { ... }) => JSX.Element | null
}This option is used when a simple asynchronous JSX element is provided. It is the preferred option when only producing a single instance and there is no need to pass properties to the component. This format simplifies the creation of closures for passing additional properties in the loader implementation.
{
loader: () => Promise<JSX.Element>;
}This option is used when a simple asynchronous component is provided. It is the preferred option when properties need to be passed to the component or multiple instance are needed, and lazy-loading is required.
{
loader: () => Promise<(props: { ... }) => JSX.Element | null>
}This option is used in the same cases as the async component loader, but when the option of synchronous loading is also needed. The structure of always having the outer loader function, even in the synchronous case, makes it possible to determine the type of the loader at runtime.
{
loader: (() => props => JSX.Element | null) | (() => Promise<props => JSX.Element | null>)
}Note that when consuming this loader we'll need to unconditionally wrap it with React.lazy. This is because you can't delay the call to React.lazy until rendering, because you're not allowed to call it within a render function. This means that we can't first call the loader to check whether the returned value is a promise or not, and we must instead unconditionally wrap it with React.lazy. Therefore the implementation of accepting one of these loaders as an option needs to look something like this:
const LazyComponent = React.lazy(() =>
Promise.resolve(options.loader()).then(loaded => ({ default: loaded })),
);We will update all APIs for the new frontend system in the @backstage/frontend-* packages.
We will not update any of the existing APIs for the old frontend system in the @backstage/core-* packages.