🚀I’ve been working with React Native for about 4 years now, and one thing I’ve learned the hard way is this:
Nothing stays permanent.
Libraries get deprecated.
Best practices change.
Things that were “recommended” last year suddenly feel wrong today.
At some point, I started asking myself:
Why am I fixing the same problem again and again, just because a library changed?🤔
The Pattern I Kept Seeing
Let’s be honest — React Native apps don’t die quickly.
They live for years.
But during that time:
- FlatList starts struggling with performance
- FlashList shows up and becomes the new standard
- SafeAreaView from react-native is no longer enough
- We move to react-native-safe-area-context
And suddenly you’re touching dozens of files for what is essentially one decision change.
That’s when I realised —
The mistake was never the library choice; it was how tightly my app depended on it.
The Simple Rule I Follow Now
- Never use core or third-party components directly across the app.
- Wrap them once. Own them.
I started creating a** top layer of base components**.
Example 1: Lists (FlatList → FlashList)
What I stopped doing
Using FlatList directly everywhere:
import { FlatList } from 'react-native';
Because I already know how this story ends.
What I do instead: AppFlatList
// AppFlatList.tsx
import React from 'react';
import { FlatList, FlatListProps } from 'react-native';
export function AppFlatList<T>(props: FlatListProps<T>) {
return (
<FlatList
{...props}
removeClippedSubviews
windowSize={5}
initialNumToRender={10}
/>
);
}
Usage stays simple:
<AppFlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
Now when performance becomes an issue (and it will), I don’t panic.
Later… switching to FlashList
// AppFlatList.tsx
import { FlashList, FlashListProps } from '@shopify/flash-list';
export function AppFlatList<T>(props: FlashListProps<T>) {
return <FlashList {...props} estimatedItemSize={60} />;
}
That’s it.
No screen changes.
No refactor weekends.
No stress.
Example 2: Base Component Deprecation (SafeAreaView)
This one hit me multiple times.
Earlier we all used:
import { SafeAreaView } from 'react-native';
Later, we were told:
“Use react-native-safe-area-context instead.”
Now imagine SafeAreaView used everywhere.
Painful, right?
// AppSafeAreaView.tsx
import React from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
export function AppSafeAreaView({ children, style }) {
return (
<SafeAreaView style={[{ flex: 1 }, style]}>
{children}
</SafeAreaView>
);
}
Usage:
<AppSafeAreaView>
<HomeScreen />
</AppSafeAreaView>
Now I don’t care:
- if API changes
- if padding logic changes
- if Android/iOS behavior differs later
- I’ve isolated it.
This Slowly Became My Default Style
Now I do this for almost everything:
- AppText → font scaling, typography updates
- AppButton → design system changes
- AppInput → validation, focus handling
- AppImage → caching, placeholders
- AppPressable → analytics hooks later
- Every time I hesitate and think
“Should I just use this directly?”
I remember how many times I’ve regretted that decision.
Why This Matters (Real Talk)
React Native evolves fast.
Your app doesn’t.
So either:
You chase changes every year
orYou design your app to absorb them calmly
Wrapping components doesn’t slow you down.
Refactoring later does.
Final Thought
This isn’t about over-engineering.
It’s about respecting the fact that change is guaranteed.
I don’t wrap components because I expect problems today.
I wrap them because I know future-me will be tired.
And honestly?
Future-me deserves better.
Top comments (0)