Skip to main content
v9 Upgrade Guide
View as Markdown

๐Ÿ‘‹ Introductionโ€‹

CDS 9 builds on the foundation laid in v8 and focuses on stability, modernization, and addressing specific debt that improves future maintainability.

Highlights of this release:

  • ๐Ÿ”ฎ React 19 support on web (with backwards compatibility for React 18)
  • ๐Ÿ“ฑ React Native 0.81 / Expo SDK 54 support on mobile
  • ๐Ÿ“ Eliminates hard-coded fixed widths/heights from all components to improve customizability and themeability
  • ๐ŸŽฏ Consolidates standalone visualization packages in order to simplify the CDS ecosystem
  • ๐ŸŒฌ๏ธ NEW migrator tool (@coinbase/cds-migrator) to help with this and future upgrades

๐Ÿ“ฆ Packagesโ€‹

The following CDS packages are receiving new versions:

  • @coinbase/cds-common@9.0.0-rc.2
  • @coinbase/cds-web@9.0.0-rc.2
  • @coinbase/cds-mobile@9.0.0-rc.2
  • @coinbase/cds-mcp-server@9.0.0-rc.2
  • @coinbase/cds-migrator@1.0.0
    New
Release Candidate Status

CDS 9 is currently in release candidate status. There are no breaking changes planned prior to the official release of 9.0.0. So it is safe to upgrade to 9.0.0-rc.2 if you want to get an early start.

In CDS 9, we are deprecating the standalone visualization packages. See the Deprecated packages for more details.

  • @coinbase/cds-web-visualization
    Deprecated
  • @coinbase/cds-mobile-visualization
    Deprecated

โœ… Upgrade Stepsโ€‹

1. Prerequisitesโ€‹

CDS 9 no longer bundles the v7 shim (cds-web/v7/cds-mobile/v7). Incremental upgrade strategies, which this pattern enabled, are no longer recommended. Starting with CDS 9, major releases will minimize breaking changes relateive to the prior version, making major upgrades possible to land in a single PR, even for large monorepos.

Before upgrading to v9:

  1. Search your codebase for any remaining cds-web/v7 / cds-mobile/v7 imports.
  2. Complete any partial migrations from the v7 shim to proper CDS 8 imports by following the v8 Migration Guide.
  3. Once all v7 imports are eliminated, you can upgrade to v9.
// โŒ Before (still on v8 with incremental migration)
import { Button } from '@coinbase/cds-web/v7/buttons/Button';

// โœ… After (must complete before upgrading to v9)
import { Button } from '@coinbase/cds-web/buttons/Button';

2. Update Packagesโ€‹

Updgrade CDS Packages
Update the CDS packages your app uses to v9.
# npm
npm install @coinbase/cds-common@9.0.0-rc.2 @coinbase/cds-web@9.0.0-rc.2

# yarn
yarn add @coinbase/cds-common@9.0.0-rc.2 @coinbase/cds-web@9.0.0-rc.2
Update CDS Peer Dependencies

No required peer dependency changes for web.


3. Run Automated Codemodsโ€‹

The new @coinbase/cds-migrator will help expedite your upgrade to CDS 9. It encapsulates individual changes in transforms, and groups related transforms into versioned presets. For upgrades from CDS 8 to CDS 9, use the v8-to-v9 preset.

For detailed CLI documentation and individual transform/preset details, see the package README.

Proactive Codemods

We will continually publish new transforms and presets to proactively address deprecations in preparation of the following major release.

Running the codemodsโ€‹

# Preview all changes (recommended first step)
npx @coinbase/cds-migrator ./src -p v8-to-v9 --dry-run

# Review the generated migration.log
cat migration.log

# Apply the migration
npx @coinbase/cds-migrator ./src -p v8-to-v9

You can also run a single transform:

npx @coinbase/cds-migrator ./src -t button-variant-values --dry-run

The migrator tool records what it has already run in .cds-migration-history.json and leaves // TODO [cds-migrator:<transform-name>] comments in the source code for cases that may need manual review.

Manual upgrade effortโ€‹

The CDS 9 codemods should address the vast majority of potential incompatibility issues, however there are a few specific edge cases that may require manual attention.

  • Tightening some TypeScript types (Carousel, Drawer, Tray, Modal, TableCell.end, etc.)
  • Updating custom Stepper progress UIs and SlideButton slot components to use Reanimated / framer-motion instead of react-spring
  • See Breaking Change Overview for more details

๐Ÿ’ฅ Breaking Change Overviewโ€‹

Hard-Coded Dimensions Removedโ€‹

CDS 9 removes hard-coded fixed heights and widths from all components. Components are now driven by their content, your layout, or theme tokens. As a side effect, several constants and utilities that exposed those fixed pixel values are deprecated (see New Deprecations). Review any surfaces that depended on CDS components occupying fixed dimensions, since those layouts may need small visual adjustments after upgrading.

Packages & Importsโ€‹

  • v7 import paths dropped โ€” @coinbase/cds-web/v7 and @coinbase/cds-mobile/v7 are no longer bundled. Apps must finish their CDS 8 upgrade before jumping to CDS 9.
  • Visualization packages merged โ€” deep paths on @coinbase/cds-(web|mobile)-visualization may no longer resolve. Use the new @coinbase/cds-(web|mobile)/visualizations/* exports.

Cutting react-spring referencesโ€‹

CDS is migrating away from react-spring, which is poorly maintained and has been the source of recurring bugs. The implementation detail leaked into the public API of two components โ€” Stepper and SlideButton. These APIs need to be adjusted before CDS can transition to the preferred animation libraries, framer-motion and react-native-reanimated.

Stepper
Web
Mobile
โ€‹

If you supply a custom step / progress UI to Stepper, the progress prop is now a plain number between 0 and 1 instead of a react-spring value. For built-in progress animation, configure timing via the new progressTimingConfig / defaultProgressTimingConfig props. The legacy progressSpringConfig prop is ignored on this animation path.

// โŒ Before (v8) โ€” progress was a SpringValue<number>
const CustomProgress = ({ progress }: { progress: SpringValue<number> }) => (
<animated.div style={{ width: progress.to((p) => `${p * 100}%`) }} />
);

// โœ… After (v9) โ€” progress is a number between 0 and 1
const CustomProgress = ({ progress }: { progress: number }) => (
<div style={{ width: `${progress * 100}%` }} />
);

// Built-in animation timing
<Stepper
progressTimingConfig={{ duration: 0.4, ease: 'easeInOut' }}
// progressSpringConfig is ignored in v9
/>;

SlideButton
Mobile
โ€‹

Custom SlideButtonBackgroundComponent and SlideButtonHandleComponent slots now receive progress as a Reanimated SharedValue<number> instead of a react-spring SpringValue<number>. Any custom slot that drove animation with spring helpers must switch to Reanimated.

// โŒ Before (v8)
import { SpringValue } from '@react-spring/native';
const BackgroundComponent = ({ progress }: { progress: SpringValue<number> }) => {
// animated.View, react-spring helpers...
};

// โœ… After (v9)
import Animated, { useAnimatedStyle, type SharedValue } from 'react-native-reanimated';

const BackgroundComponent = ({ progress }: { progress: SharedValue<number> }) => {
const style = useAnimatedStyle(() => ({ opacity: progress.value }));
return <Animated.View style={style} />;
};

Spring config: animationConfig on DefaultSlideButtonHandle is deprecated. Use slideButtonSpringConfig together with Reanimated's withSpring.

Layout: SlideButton now applies default heights (40 for compact, 56 for regular) and derives the handle's minWidth from height. If you previously omitted height and relied on the old interactable-height + spring-driven sizing, double-check the rendered layout โ€” you may need to set height explicitly to preserve v8 behavior.

TypeScript Type Changesโ€‹

A handful of CDS types have been tightened to better reflect runtime behavior or to align with React 19's updated typedefs. These changes are runtime-compatible but may surface as TypeScript errors after you upgrade.

Tip

In most cases, removing explicit type annotations and letting TypeScript infer the type from CDS is the simplest fix.

React.FC type removed for render-children types
Web
Mobile
โ€‹

The following components' render functions are now typed as (props) => ReactNode instead of using the deprecated React.FC type:

  • CarouselItem
  • Drawer
  • Tray
  • Modal

TableCell.end and SelectOptionGroupComponent.accessory
Web
โ€‹

Both components' props now require a stricter type: React.ReactElement<CellAccessoryProps>.

// โŒ Before (v8) โ€” was previously typed as ReactElement/ReactNode
<TableCell end={<div>123</div>} />;

// โœ… After (v9)
import { CellTextAccessory } from '@coinbase/cds-web/cells';
<TableCell end={<CellTextAccessory text="123" />} />;

Input onFocus / onBlur Event Types
Mobile
โ€‹

Certain mobile component APIs that accept focus/blur event handlers now use simpler typing

// โŒ Before (v8)
import type { NativeSyntheticEvent, TextInputFocusEventData } from 'react-native';
const onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
/* ... */
};

// โœ… After (v9)
import type { FocusEvent } from 'react-native';
const onFocus = (e: FocusEvent) => {
/* ... */
};

Removed APIs (Dropped Deprecations)โ€‹

The following @deprecated APIs are fully removed in v9.

PackageDropped deprecations
Common
emptyObject, StickyFooterProps
Web
AnnouncementCardProps, FeatureEntryCardProps, RadioGroupProps, CheckboxGroupProps, FeedCardProps, IconCounterButton.iconSize
Mobile
AnnouncementCardProps, FeatureEntryCardProps, RadioGroupProps, CheckboxGroupProps, FeedCardProps, LinearGradient.isBelowChildren, IconCounterButton.iconSize

โš ๏ธ New Deprecationsโ€‹

The following APIs are still available in v9 but are scheduled for removal in CDS 10. Migrators and/or AI agents will be released following the v9 launch to help with each. You can also start migrating manually using the guidance below.

Packagesโ€‹

PackageNotes
@coinbase/cds-web-visualizationUse @coinbase/cds-web/visualizations/{chart,sparkline} instead. Remove the dependency once your imports are migrated.
@coinbase/cds-mobile-visualizationUse @coinbase/cds-mobile/visualizations/{chart,sparkline} instead. Remove the dependency once your imports are migrated.
Codemod

The CDS migrator's v8-to-v9 preset will update these packages' import paths for you.

Componentsโ€‹

DeprecationPackageNotes
AvatarButton border props
Web
Mobile
Border-related props have no effect; remove them.
IconCounterButton.dangerouslySetColor
Web
Mobile
Use styles.icon, classNames.icon, or color on web. Use styles.icon or color on mobile.
Spinner
Web
Mobile
Prefer indeterminate ProgressCircle and ActivityIndicator on mobile where documented.
Stepper.progressSpringConfig / Stepper.defaultProgressSpringConfig
Web
Mobile
Use progressTimingConfig / defaultProgressTimingConfig.
Text.dangerouslySetColor
Web
Mobile
Use style, className, or color on web. Use style or the color style prop on mobile.
TextBody, TextCaption, TextDisplay*, TextHeadline, TextInherited, TextLabel*, TextLegal, TextTitle*
Web
Mobile
Use Text with the matching font.
DefaultSlideButtonHandle.animationConfig
Mobile
Use slideButtonSpringConfig with Reanimated withSpring.

Hooks & Functionsโ€‹

DeprecationPackageNotes
getButtonSpacingProps
Common
Going away without a built-in replacement. Handle button spacing locally or with your own helper.
getDotSize
Common
Fixed pixel sizing for dots is no longer provided as a shared helper.
useMergeRefs
Common
Use mergeRefs from @coinbase/cds-common/utils/mergeRefs. Automated by migrate-use-merge-refs-import.
usePopper
Web
Temporary compatibility shim. Use Floating UI directly in your app if you still need popper-like positioning.
useStatusBarHeight
Mobile
Use useSafeAreaInsets().top from react-native-safe-area-context.
useHasNotch
Mobile
Replace with useSafeAreaInsets().top > 20 from react-native-safe-area-context if you need an equivalent.

Tokens & Constantsโ€‹

The following tokens were fixed pixel values that pin component sizing. With v9 removing hard-coded heights/widths, they are no longer published. Define your own values or rely on layout-driven sizing.

DeprecationPackageNotes
[tokens/cell] compactListHeight, listHeight, selectOptionHeight
Common
Define your own values or rely on layout-driven sizing.
[tokens/dot] dotSizeTokens
Common
Define your own values or rely on layout-driven sizing.

Typesโ€‹

DeprecationPackageNotes
MobileBannerProps
Mobile
Use BannerProps.
[types/BoxBaseProps] PositionStyles
Common
Prefer PositionStyles from mobile styleProps or web styleProps where the guidance applies.
CardHeaderProps
Common
Use ContentCardHeaderProps for content cards.
CardMediaProps
Common
Use SpotSquare, Pictogram, or RemoteImage by type, per JSDoc.
DimensionStyles value types
Common
Prefer dimension props with 'auto' | number | string.
[types/Position] Position
Common
Use CSSProperties['position'] on web or ViewStyle['position'] on React Native.

Is this page useful?

Coinbase Design is an open-source, adaptable system of guidelines, components, and tools that aid the best practices of user interface design for crypto products.