Strict TypeScript API
New in 0.87: Enabled by default (previously an opt-in)
- If you need to opt out from the Strict API (e.g. in a complex app), see the guide below.
- Use the
/migrate-to-strict-apiskill to migrate your codebase via an agent. - If you're getting started with a new app, skip this page.
The Strict TypeScript API is React Native's modern TypeScript API, replacing the previous hand-maintained TypeScript definitions from earlier versions.
Key changes (breaking)
- No deep imports. The API is restricted to
react-native's index file. This is a tighter and more intentional public API contract. It also ensures that internal file path changes in React Native's source code won't be breaking. - Generated directly from source. Previously, React Native used separately maintained manual types. Generating from source now means we improve coverage, correctness, and compatibility guarantees.
Opting out
The Strict API is a breaking change, and not all apps and libraries may be able to migrate immediately.
We continue to ship our previous manual types, which can be reverted to via your tsconfig.json config. Please note that in a future release we will remove this opt out.
{
"extends": "@react-native/typescript-config",
"compilerOptions": {
...
+ "customConditions": ["react-native-legacy-deep-imports"]
}
}
The 0.87 rollout of the Strict API by default is an ecosystem-wide change, and one we need to make in order to have stable API guarantees for React Native in future.
Design choices about which APIs are exported at root, and intentional updates to type names/shapes have been worked on with the community and partners since the original 0.80 preview.
We believe we've ironed out all rough edges, but there may still be edge cases. If you're choosing to opt out of the Strict API in 0.87 or later, we'd love to know why: discussion thread.
Migration guide
New CodegenTypes namespace
Types used for codegen, like Int32, Double, WithDefault etc. are now available under a single CodegenTypes namespace. Similarly, codegenNativeComponent and codegenNativeCommands are now available to import from the react-native package instead of using the deep import.
Namespaced CodegenTypes as well as codegenNativeCommands and codegenNativeComponent are also available from react-native package when the Strict API is not enabled to make the adoption easier for third-party libraries.
Migration
- Before
- After
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type {
Int32,
WithDefault,
} from 'react-native/Libraries/Types/CodegenTypes';
interface NativeProps extends ViewProps {
enabled?: WithDefault<boolean, true>;
size?: Int32;
}
export default codegenNativeComponent<NativeProps>(
'RNCustomComponent',
);
import {CodegenTypes, codegenNativeComponent} from 'react-native';
interface NativeProps extends ViewProps {
enabled?: CodegenTypes.WithDefault<boolean, true>;
size?: CodegenTypes.Int32;
}
export default codegenNativeComponent<NativeProps>(
'RNCustomComponent',
);
Refs now use *Instance types
Each built-in component now has a dedicated *Instance type for use with refs — for example, ViewInstance, TextInputInstance, ScrollViewInstance. These are the recommended way to type refs under the Strict TypeScript API.
Previously, useRef<View> worked because View and other components were typed as a class. Under the Strict API, built-in components are typed as functions, so View refers to the function itself — component type names no longer work as ref types.
- Before
- After
import {useRef} from 'react';
import {View, TextInput} from 'react-native';
function MyComponent() {
const viewRef = useRef<View>(null);
const inputRef = useRef<TextInput>(null);
return (
<>
<View ref={viewRef} />
<TextInput ref={inputRef} />
</>
);
}
import {useRef} from 'react';
import type {
TextInput,
TextInputInstance,
View,
ViewInstance,
} from 'react-native';
function MyComponent() {
const viewRef = useRef<ViewInstance>(null);
const inputRef = useRef<TextInputInstance>(null);
return (
<>
<View ref={viewRef} />
<TextInput ref={inputRef} />
</>
);
}
*Instance types also work transparently with Animated variants — no separate type is needed:
const viewRef = useRef<ViewInstance>(null);
<View ref={viewRef} />
<Animated.View ref={viewRef} />
This also replaces the removed Animated.LegacyRef type. Code using ref={ref as React.Ref<Animated.LegacyRef<View>>} can be simplified to ref={ref} with a ViewInstance-typed ref.
Available instance types
| Component | Instance type |
|---|---|
ActivityIndicator | ActivityIndicatorInstance |
Button | ButtonInstance |
DrawerLayoutAndroid | DrawerLayoutAndroidInstance |
FlatList | FlatListInstance |
Image | ImageInstance |
ImageBackground | ImageBackgroundInstance |
KeyboardAvoidingView | KeyboardAvoidingViewInstance |
Modal | ModalInstance |
Pressable | PressableInstance |
ProgressBarAndroid | ProgressBarAndroidInstance |
RefreshControl | RefreshControlInstance |
SafeAreaView | SafeAreaViewInstance |
ScrollView | ScrollViewInstance |
SectionList | SectionListInstance |
StatusBar | StatusBarInstance |
Switch | SwitchInstance |
Text | TextInstance |
TextInput | TextInputInstance |
TouchableHighlight | TouchableHighlightInstance |
TouchableNativeFeedback | TouchableNativeFeedbackInstance |
TouchableOpacity | TouchableOpacityInstance |
View | ViewInstance |
VirtualizedList | VirtualizedListInstance |
VirtualizedSectionList | VirtualizedSectionListInstance |
Components without ref support (InputAccessoryView, TouchableWithoutFeedback, experimental_LayoutConformance) do not have instance types.
Migration
| Before | After |
|---|---|
useRef<View>(null) | useRef<ViewInstance>(null) |
useRef<React.ComponentRef<typeof View>>(null) | useRef<ViewInstance>(null) |
useRef<HostInstance>(null) (for a specific component) | useRef<ViewInstance>(null) |
Ref<Animated.LegacyRef<View>> | Ref<ViewInstance> |
React.ComponentRef<typeof View> remains valid and produces the same type as ViewInstance. The *Instance types are convenient aliases — both approaches work.
Removal of *Static types
Migration
- Before
- After
import {Linking, LinkingStatic} from 'react-native';
function foo(linking: LinkingStatic) {}
foo(Linking);
import {Linking} from 'react-native';
function foo(linking: Linking) {}
foo(Linking);
The following APIs were previously named as *Static plus a variable declaration of said type. In most cases there was an alias so that value and the type were exported under the same identifier, but some were missing.
Affected APIs
AlertStaticActionSheetIOSStaticToastAndroidStaticInteractionManagerStatic(In this case there was no relevantInteractionManagertype alias)UIManagerStaticPlatformStaticSectionListStaticPixelRatioStatic(In this case there was no relevantPixelRatiotype alias)AppStateStaticAccessibilityInfoStaticImageResizeModeStaticBackHandlerStaticDevMenuStatic(In this case there was no relevantDevMenutype alias)ClipboardStaticPermissionsAndroidStaticShareStaticDeviceEventEmitterStaticLayoutAnimationStaticKeyboardStatic(In this case there was no relevantKeyboardtype alias)DevSettingsStatic(In this case there was no relevantDevSettingstype alias)I18nManagerStaticEasingStaticPanResponderStaticNativeModulesStatic(In this case there was no relevantNativeModulestype alias)LogBoxStaticPushNotificationIOSStaticSettingsStaticVibrationStatic
Other breaking changes
Changes to Animated types
Animated nodes were previously generic types based on their interpolation output. Now, they are non-generic types with a generic interpolate method.
Animated.LegacyRef is no longer available. Use the appropriate *Instance type instead (e.g. ViewInstance for Animated.View).
Unified types for optional props
In the new types, every optional prop will be typed as type | undefined.
Removal of some deprecated types
All types listed in DeprecatedPropertiesAlias.d.ts are inaccessible under the Strict API.
Removal of leftover component props
Some properties that were defined in type definitions but were not used by the component or were lacking a definition were removed (for example: lineBreakMode on Text, scrollWithoutAnimationTo on ScrollView, transform styles defined outside of transform array).
Previously accessible private type helpers may now be removed
Due to the configuration of the previous type definitions, every defined type was accessible from the react-native package. This included types that were not explicitly exported and helper types that were only supposed to be used internally.
Notable examples of this are types related to StyleSheet (like RecursiveArray, RegisteredStyle and Falsy) and Animated (like WithAnimatedArray and WithAnimatedObject).