Create and customize component catalogs

In the A2UI architecture, every surface is driven by a component catalog. A catalog is a formal contract that defines the UI components, property schemas, and local client functions available to an AI agent. Rather than generating arbitrary code or inventing unknown elements, the agent must construct user interfaces using exclusively the components declared in the catalog.

In other words: the catalog declares the components, and the agent uses them to build your app's UI.

When building an Android app with the Jetpack Compose A2UI renderer, you have flexible options for how you provide catalogs:

  • The Basic Catalog: The A2UI project defines a standardized, general-purpose specification called the Basic Catalog, which includes common elements like buttons, text, text fields, cards, and lists. The AndroidX library androidx.compose.material3:material3-a2ui provides an out-of-the-box implementation of this Basic Catalog specification using native Material Design 3 components. The androidx.a2ui.compose:compose-ui library also provides a generic schema definition for the Basic Catalog, which helps you implement the Basic Catalog for your own design system.
  • Custom catalogs: For production applications with their own distinct design systems, you can build a custom catalog from scratch. This restricts the agent to the exact components, styling tokens, and visual language of your app.
  • A subset or hybrid: You can combine specific component implementations from the provided Basic Catalog with your own custom components, or override individual component implementations within the Basic Catalog suite.

Use the provided Basic Catalog

To get started quickly without authoring a component schema from scratch, you can use the provided implementation of the A2UI Basic Catalog specification. The androidx.compose.material3:material3-a2ui library implements the Basic Catalog using Material Design 3 components.

When you instantiate materialA2uiBasicCatalogV1, supply renderers and handlers for the following:

  • Media components, such as images, video, and audio players
  • URL opener
  • Localized message formatting

The A2UI libraries intentionally don't bundle external media and networking dependencies, such as Coil, Glide, or Media3. Instead, you provide your own renderers. This prevents dependency conflicts by providing unique libraries. For example, if your app already uses Coil for image loading or Media3 for playback, you can plug those existing libraries directly into the catalog.

The following example demonstrates how to instantiate the Basic Catalog and wire up your preferred media libraries, URL opener, and message formatter:

// Instantiate the provided Basic Catalog (implemented with Material 3)
val basicCatalog = materialA2uiBasicCatalogV1(
    // Example: Wire up Coil for image loading (via AsyncImage)
    image = MaterialA2uiBasicCatalogV1Defaults.image {
            url, description, scale, modifier, onError ->
        AsyncImage(
            model = url,
            contentDescription = description,
            contentScale = scale,
            modifier = modifier,
            onError = { state -> onError(state.result.throwable) },
        )
    },

    // Example: Use ExoPlayer/Media3 for video
    video = MaterialA2uiBasicCatalogV1Defaults.video { url, modifier, onError ->
        // Custom ExoPlayer video integration here
    },

    // Example: Use an audio player
    audioPlayer = MaterialA2uiBasicCatalogV1Defaults.audioPlayer {
            url, description, modifier, onError ->
        // Custom audio integration here
    },

    // Handle outbound URLs, such as using an app navigator or context intents.
    urlOpener = { url ->
        appNavigator.openUrl(url)
    },

    // Handle localized message formatting
    messageFormatter = { pattern, locale, args ->
        MessageFormat.format(context, locale, pattern, args)
    },
    localeProvider = A2uiLocaleProvider.Default,
)

Assemble a custom component catalog from scratch

If your app uses a custom design system, you can define your own catalog containing your own custom A2uiComponent implementations. This approach gives you full control over the component schemas exposed to the agent and the native Compose UI emitted:

// Define a custom catalog that mirrors your app's design system
val CustomDesignSystemCatalog = A2uiCatalog(
    catalogId = "https://example.com/catalogs/my-design-system/v1/catalog.json",
    components = listOf(
        CustomButtonComponent,
        CustomCardComponent,
        CustomTextFieldComponent,
    ),
    functions = listOf(MyCustomLocalFunction()),
)

For instructions on defining individual component schemas and their Compose UI rendering logic, see Implement custom A2UI components.

Use a subset of Basic Catalog components with custom components

You don't have to choose strictly between building everything from scratch or adopting the entire Basic Catalog. You can assemble a catalog that combines select components from the provided Basic Catalog implementation with your own custom components:

// Assemble a catalog using select Basic Catalog components alongside custom components
val hybridCatalog = A2uiCatalog(
    catalogId = "https://example.com/catalogs/my-app/v1/catalog.json",
    components = listOf(
        // Use provided Basic Catalog components (built with Material 3)
        MaterialA2uiBasicCatalogV1Defaults.text,
        MaterialA2uiBasicCatalogV1Defaults.card,

        // Add proprietary components from your app's design system
        CustomChartComponent,
        CustomProductCardComponent,
    ),
    functions = createBasicCatalogFunctions(...),
)

Alternatively, you can customize the provided Basic Catalog suite by overriding specific component slots:

// Override specific components within the Basic Catalog suite
val customizedBasicCatalog = materialA2uiBasicCatalogV1(
    // Supply required renderers (such as Coil or ExoPlayer) as shown earlier
    image = MaterialA2uiBasicCatalogV1Defaults.image(myImageRenderer),
    video = MaterialA2uiBasicCatalogV1Defaults.video(myVideoRenderer),
    audioPlayer = MaterialA2uiBasicCatalogV1Defaults
        .audioPlayer(myAudioRenderer),
    urlOpener = { url -> /* Open URL */ },
    messageFormatter = { pattern, _, _ -> pattern },
    localeProvider = A2uiLocaleProvider.Default,
    // Replaces the default button. If you use this, implement the
    // A2uiBasicCatalogV1.Button interface.
    button = MyCustomBrandButtonComponent,
    
)

Manage catalog versioning and schema evolution

A2UI catalogs are explicitly versioned based on their JSON schema contract. A version bump is required when introducing breaking schema changes:

// Original component (v1 catalog)
object CustomButtonComponent : A2uiComponent { ... }

// Unchanged component across versions
object CustomTextComponent : A2uiComponent { ... }

// Future breaking schema change (v2 catalog)
object CustomButtonComponentV2 : A2uiComponent { ... }

// Assembles the v1 catalog
fun customCatalogV1(
    button: A2uiComponent = CustomButtonComponent,
    text: A2uiComponent = CustomTextComponent,
): A2uiCatalog = A2uiCatalog(
    catalogId = "https://example.com/catalogs/my-app/v1/catalog.json",
    components = listOf(button, text),
)

// Assembles the v2 catalog
fun customCatalogV2(
    button: A2uiComponent = CustomButtonComponentV2,
    text: A2uiComponent = CustomTextComponent,
): A2uiCatalog = A2uiCatalog(
    catalogId = "https://example.com/catalogs/my-app/v2/catalog.json",
    components = listOf(button, text),
)

To enable seamless migrations without downtime, your client can register multiple supported catalog versions with the message processor simultaneously:

private val processor = A2uiMessageProcessor(
    catalogs = listOf(
        customCatalogV1(),
        customCatalogV2(),
    ),
)

During catalog negotiation, the agent discovers all supported catalog IDs and targets the appropriate version for each surface.

Implementation details

The following sections explain internal catalog validation and schema negotiation.

The catalog management user journeys introduce the following key APIs:

  • A2uiCatalog: Interface and top-level factory function for defining component catalogs.
  • materialA2uiBasicCatalogVX: Versioned factory functions (such as materialA2uiBasicCatalogV1) that provide the Material 3 implementation of the standard A2UI Basic Catalog specification.
  • A2uiReadinessEvaluator and asReadinessEvaluator(): A2uiReadinessEvaluator is the interface for evaluating component readiness. The asReadinessEvaluator() extension function resolves readiness states using the components registered in a catalog.

A2UI version catalog and component schemas

A catalog schema definition is associated with a specific protocol version. When the protocol evolves, the catalog definition advances its version. Component implementations for this next version can use updated renderer APIs while lower versions remain operational side by side.