Skip to main content

Migration Guide

Migrating to 1.2.3 (from 1.2.2)

ScrollContainer no longer fills its parent by default

Previously, ScrollContainer always applied flexGrow: 1 internally, even without a flex prop — this was undocumented and invisible on a normal full-screen scroll view, but caused unrelated bugs (e.g. Popup expanding to fill the whole screen when it contained a ScrollContainer). ScrollContainer now only grows when flex is explicitly passed.

If you have a ScrollContainer that relies on filling the remaining space of its parent (the common "main scroll area of a screen" pattern) and it doesn't already pass flex, add flex={1}:

// Before
<Container flex={1}>
<ScrollContainer padding={16}>
{/* was implicitly filling the screen */}
</ScrollContainer>
</Container>

// After
<Container flex={1}>
<ScrollContainer flex={1} padding={16}>
{/* now explicit */}
</ScrollContainer>
</Container>

If you were relying on ScrollContainer sizing to its content (e.g. nested inside Popup), no change is needed — that's now the default.

Migrating to 1.2.2 (from 1.2.1)

Popup's type prop removed

Presentation is now automatic and platform-based: Popup always renders as a bottom sheet on native and always as a centered dialog on web. Remove any type prop passed to Popup. Message, which is built on Popup, loses its type prop for the same reason.

// Before
<Popup type="sheet" title="Select Option" renderToggleButton={...}>
...
</Popup>

// After
<Popup title="Select Option" renderToggleButton={...}>
...
</Popup>

If you need to know which presentation is active (e.g. to conditionally apply bottom safe-area insets), read the resolved value from usePopup().type (or the usePopup().isDialog shorthand) instead — it just can no longer be set.

New required peer dependency: @swmansion/react-native-bottom-sheet

Popup now renders as a native bottom sheet using @swmansion/react-native-bottom-sheet on native platforms. Install it alongside your other peer dependencies:

npx expo install @swmansion/react-native-bottom-sheet

No other setup is required — MultinaireUI wraps its children in the necessary provider automatically.

Migrating to 1.0.7 (from 1.0.6)

Package renamed: @multinaire/ui@multinaire/expo-ui

This is a breaking change release. Update the dependency and every import:

# Remove old package
npm uninstall @multinaire/ui

# Install new package
npm install @multinaire/expo-ui
// Before
import MultinaireUI, { Button, Typography } from '@multinaire/ui';

// After
import MultinaireUI, { Button, Typography } from '@multinaire/expo-ui';

Also update:

  • Any TranslationSchema augmentation:

    // Before
    declare module '@multinaire/ui' { ... }

    // After
    declare module '@multinaire/expo-ui' { ... }
  • tsconfig.json path mappings, if you use the example app pattern:

    // Before
    "paths": {
    "@multinaire/ui": ["../build/index"]
    }

    // After
    "paths": {
    "@multinaire/expo-ui": ["../build/index"]
    }

Expo SDK 56 required

The library now targets Expo SDK 56. Upgrade your app before updating:

npx expo install expo@^56.0.0 react-native react-native-reanimated react-native-worklets react-native-screens react-native-svg

See the Expo SDK 56 upgrade guide if you're coming from SDK 55 or earlier.

@react-navigation/native no longer required

If you installed @react-navigation/native solely to satisfy this package's peer dependencies, you can remove it — navigation types are now sourced through expo-router:

npm uninstall @react-navigation/native

No code changes are required; this only affects your package.json.

Migrating to 1.0.4 (from 1.0.3)

AnimatedTouchableOpacity renamed to AnimatedPressable

All interactive components moved from TouchableOpacity to Pressable to enable hover support on web/desktop. The exported AnimatedTouchableOpacity component was renamed accordingly. If you imported it directly, update the name:

// Before
import { AnimatedTouchableOpacity } from '@multinaire/ui';

<AnimatedTouchableOpacity activeOpacity={0.8} onPress={onPress} />;

// After
import { AnimatedPressable } from '@multinaire/ui';

<AnimatedPressable onPress={onPress} />;

AnimatedPressable is an Animated-wrapped Pressable, so the activeOpacity prop no longer applies. For the same hover/press opacity feedback the built-in components use, combine it with the new usePressableInteraction hook:

import { AnimatedPressable, usePressableInteraction } from '@multinaire/ui';

function MyButton({ onPress }) {
const { animatedStyle, overrideProps } = usePressableInteraction();

return (
<AnimatedPressable style={animatedStyle} onPress={onPress} {...overrideProps} />
);
}

Migrating to 1.0.3 (from 1.0.2)

useScreenOptions — deprecated properties removed

The stack, stackWithOverrides, topTab, topTabWithOverrides, bottomTab, bottomTabWithOverrides, sideBar, and sideBarWithOverrides properties have been removed. If you haven't already migrated away from them, update your navigators now:

// Before (removed)
const { stack, bottomTab, topTabWithOverrides } = useScreenOptions();

<Stack screenOptions={stack} />
<Tabs screenOptions={bottomTab} />
<Tabs screenOptions={topTabWithOverrides({ lazy: false })} />

// After
const { screenOptions } = useScreenOptions('stack');
const { screenOptions: tabOptions } = useScreenOptions('bottom-tab');
const { screenOptions: topOptions } = useScreenOptions('top-tab');

<Stack screenOptions={screenOptions()} />
<Tabs screenOptions={tabOptions()} />
<Tabs screenOptions={topOptions({ lazy: false })} />

Localization — 'system' renamed to 'auto'

The 'system' value for defaultLanguage has been replaced with 'auto'. If you passed defaultLanguage="system" explicitly, rename it:

// Before
<MultinaireUI defaultLanguage="system" translations={...}>

// After
<MultinaireUI defaultLanguage="auto" translations={...}>

'auto' is still the default, so no change is needed if you relied on the default behaviour.

The language and changeLanguage types in LocalizationContext and useLocalization no longer include 'system'. The languages array returned by useLocalization now contains only concrete locale codes — remove any 'system' entry handling in language pickers:

// Before — filtering out 'system' before rendering
const { languages } = useLocalization();
const displayLanguages = languages.filter(l => l !== 'system');

// After — languages only contains locale codes, no filtering needed
const { languages } = useLocalization();

Migrating to 1.0.2 (from 1.0.1)

Toggle default color

The color prop default changed from 'primary' to 'success'. If you relied on the old default, pass color="primary" explicitly:

// Before (implicit primary)
<Toggle value={isEnabled} onChange={setIsEnabled} />

// After — add color prop to keep the old appearance
<Toggle value={isEnabled} color="primary" onChange={setIsEnabled} />

useScreenOptions — new API

useScreenOptions now accepts a navigator type and returns a screenOptions function together with title(), tabBarLabel(), and tabBarIcon() helpers. The old property-based API still works but is deprecated and will be removed in a future minor version.

// Before (deprecated)
const { stack, topTab, bottomTabWithOverrides } = useScreenOptions();

<Stack screenOptions={stack} />
<Tabs screenOptions={topTab} />
<Tabs screenOptions={bottomTabWithOverrides({ tabBarActiveTintColor: 'red' })} />

// After
const { screenOptions } = useScreenOptions('stack');
const { screenOptions: tabOptions } = useScreenOptions('top-tab');
const { screenOptions: bottomOptions } = useScreenOptions('bottom-tab');

<Stack screenOptions={screenOptions()} />
<Tabs screenOptions={tabOptions()} />
<Tabs screenOptions={bottomOptions({ tabBarActiveTintColor: 'red' })} />

The title(), tabBarLabel(), and tabBarIcon() helpers make screen options fully type-safe when TranslationSchema is augmented:

const { screenOptions, title, tabBarIcon } = useScreenOptions('bottom-tab');

<Tabs.Screen
name="home"
options={{
...screenOptions(),
title: title('home'), // type-checked against TranslationSchema
tabBarIcon: tabBarIcon('Home'),
}}
/>

createTabSceenOptions renamed

The internal export createTabSceenOptions (typo) has been renamed to createTabScreenOptions. Update any direct imports:

// Before
import { createTabSceenOptions } from '@multinaire/ui';

// After
import { createTabScreenOptions } from '@multinaire/ui';

Type-safe i18n with TranslationSchema (opt-in)

String props across all components now accept TranslationKey. To unlock compile-time key checking, augment the TranslationSchema interface once in your project:

// i18n.d.ts (in your app root)
import en from '@/assets/translations/en.json';
import fr from '@/assets/translations/fr.json';

declare module '@multinaire/ui' {
interface TranslationSchema {
en: typeof en;
fr: typeof fr;
}
}

After this, passing an unknown string to any component title, placeholder, or errorText prop will produce a TypeScript error. This is entirely opt-in — without the augmentation all string props accept string as before.

ContainersafeAreaEdges + onPress are mutually exclusive

Combining both props now throws a runtime error. Wrap the SafeAreaView in a separate pressable instead:

// Before (silently broken layout)
<Container safeAreaEdges={['top']} onPress={handlePress}>
{children}
</Container>

// After
<Container safeAreaEdges={['top']}>
<Container onPress={handlePress}>
{children}
</Container>
</Container>

Migrating to 1.0.1 (from 1.0.0)

useUIuseTheme

The hook was shipped under the wrong name in 1.0.0. Replace every usage:

// Before
const { colors, variables, fonts } = useUI();

// After
const { colors, variables, fonts } = useTheme();

Migrating to 1.0.0 (from 0.3.x)

This is a breaking change release. The package has been renamed and all Multinaire prefixes have been stripped from the public API.

1. Update the package name

# Remove old package
npm uninstall @multinaire/multinaire-design

# Install new package
npm install @multinaire/ui

2. Update all imports

Replace every import from @multinaire/multinaire-design with @multinaire/ui:

// Before
import MultinaireDesignProvider, {
useMultinaireTheme,
useMultinaireLocalization,
MultinaireButton,
MultinaireText,
} from '@multinaire/multinaire-design';

// After
import MultinaireUI, {
useTheme,
useLocalization,
Button,
Typography,
} from '@multinaire/ui';

3. Rename every symbol

Use the tables below as a find-and-replace reference.

Root provider

BeforeAfter
MultinaireDesignProviderMultinaireUI
MultinaireDesignPropsMultinaireUIProps

Hooks

BeforeAfter
useMultinaireThemeuseTheme
useMultinaireThemeModeuseThemeMode
useMultinaireLocalizationuseLocalization
useMultinaireModalusePopup
useMultinaireKeyboarduseKeyboard
useMultinaireResponsiveDesignuseResponsiveDesign
useMultinaireLoadingAnimationuseAnimate

Icons

BeforeAfter
MultinaireIconsIcons

Components with intent-based renames

BeforeAfter
MultinaireTextTypography
MultinaireImagePhoto
MultinaireLoadingAnimate
MultinaireScrollViewScrollContainer
MultinaireSwitchToggle
MultinaireModalPopup
MultinaireModalHeaderPopupHeader
MultinaireModalToggleButtonPopupToggleButton
MultinaireKeyboardAvoidingViewKeyboardAvoidingContainer
MultinaireListViewListContainer
MultinairePageViewPageContainer
MultinaireTabViewTabContainer

Components — prefix-only strip

BeforeAfter
MultinaireContainerContainer
MultinaireCardCard
MultinaireBadgeBadge
MultinaireDividerDivider
MultinaireGapGap
MultinaireIconIcon
MultinairePositionedPositioned
MultinaireAvatarAvatar
MultinaireMessageMessage
MultinaireButtonButton
MultinaireIconButtonIconButton
MultinaireActionButtonActionButton
MultinaireMenuButtonMenuButton
MultinaireSocialLoginButtonSocialLoginButton
MultinaireFloatingActionButtonFloatingActionButton
MultinaireInputInput
MultinairePickerButtonPickerButton
MultinaireMediaPickerButtonMediaPickerButton
MultinaireCheckboxCheckbox
MultinairePaginationPagination
MultinairePagePage
MultinaireTopTabTopTab
MultinaireBottomTabBottomTab
MultinaireListPickerListPicker
MultinaireListPickerItemListPickerItem
MultinaireDateTimePickerDateTimePicker
MultinaireDialogDialog
MultinaireMenuMenu
MultinaireStackHeaderStackHeader
MultinaireTabBarTabBar
MultinaireTabHeaderTabHeader
MultinaireSideBarSideBar

Prop types — drop the Multinaire prefix and are renamed to match their component names exactly:

OldNew
TextPropsTypographyProps
ImagePropsPhotoProps
SwitchPropsToggleProps
KeyboardAvoidingViewPropsKeyboardAvoidingContainerProps
ScrollViewPropsScrollContainerProps
ListViewPropsListContainerProps
TabViewPropsTabContainerProps
PageViewPropsPageContainerProps
ListPickerItemListPickerItemData
DatePickerPropsDateTimePickerProps

Removed props

MenuButton.type ('default' | 'warning' | 'error') has been removed — foreground color is now always onBackground:

// Before
<MenuButton type="error" leading="Logout" title="Sign Out" onPress={signOut} />

// After
<MenuButton leading="Logout" title="Sign Out" onPress={signOut} />

ButtonType.destructive has been removed — use 'error' instead:

// Before
<Button type="destructive" title="Delete" />

// After
<Button type="error" title="Delete" />

4. Update tsconfig paths (if using the example app pattern)

// Before
"paths": {
"@multinaire/multinaire-design": ["../build/index"]
}

// After
"paths": {
"@multinaire/ui": ["../build/index"]
}