My Developer Account

Upgrading Your Add-On

This guide covers the steps needed to upgrade your add-on when changes are introduced to the add-ons hosting architecture or hosted components within Planning Center. Changes of this nature will generally be backwards compatible, meaning your add-on will continue to work, but we'll need you to update your add-on within a reasonable timeframe.

Support Timelines

Feature Supported until Upgrade Guide
@remote-ui 2027-01-01 Upgrading from @remote-ui to @remote-dom
component_version: "5" 2027-01-01 Upgrading from component_version 5 to 6

Upgrading from @remote-ui to @remote-dom

@remote-dom is Shopify's updated version of @remote-ui, the library that powers add-on rendering. Instead of importing from @remote-ui directly, you import from @planningcenter/add-ons/integrator and get fully typed components without the string-casting boilerplate. Your add-on's behavior is otherwise unchanged.

Replace your @remote-ui/react import and component string declarations with direct imports from @planningcenter/add-ons/integrator, and replace the onRender callback with renderExtension:

Before (@remote-ui)

import React, { useState } from "react"
import { createRoot } from "@remote-ui/react"
import type { components } from "@planningcenter/add-ons"

const Button = "Button" as unknown as (typeof components)["Button"]
const Heading = "Heading" as unknown as (typeof components)["Heading"]

function App() {
  const [counter, setCounter] = useState(0)

  return (
    <>
      <Heading text="Hello from my UI extension" />
      <Button
        onClick={() => setCounter(counter + 1)}
        text={`Click count: ${counter}`}
      />
    </>
  )
}

interface IUiExtensionWindow {
  onRender: (callback: (root: any, args: any) => any) => any
}

const window = self as IUiExtensionWindow
window.onRender((root, args) => {
  const remoteRoot = createRoot(root)
  remoteRoot.render(<App {...args} />)
  root.mount()
})

After (@remote-dom)

import React, { useState } from "react"
import {
  Button,
  Heading,
  renderExtension,
} from "@planningcenter/add-ons/integrator"

function App() {
  const [counter, setCounter] = useState(0)

  return (
    <>
      <Heading text="Hello from my UI extension" />
      <Button
        onClick={() => setCounter(counter + 1)}
        text={`Click count: ${counter}`}
      />
    </>
  )
}

renderExtension(App)









To build and upload your bundle, use @planningcenter/add-ons-cli version 3.0.0 or later.

Upgrading from component_version 5 to 6

After updating your add-on's code to use the v6 component API, set component_version: "6" on each UI Extension entry in your config.yaml.

Box

In v5, Box colored its background and border by accepting semantic theme names (success, danger, etc.) on backgroundColor and borderColor props. Theming both required passing the same theme name to each. v6 moves semantic theming to a dedicated theme prop, and opens backgroundColor and borderColor to receive design-system color tokens.

Replace any backgroundColor or borderColor usage that passed a theme name with the theme prop:

Before (v5)

<Box backgroundColor="info" borderColor="info">
  ...
</Box>

After (v6)

<Box theme="info">
  ...
</Box>

If you want to apply colors outside the semantic themes, you can now pass any design-system color token directly:

Before (v5)

// unsupported


After (v6)

<Box backgroundColor="neutral70" borderColor="neutral40">
  ...
</Box>

For the full list of changes in each version, see the component library changelog.