TL;DR: I got CSS Grid working natively in React Native by applying an experimental patch to Yoga (the layout engine). Here’s what I learned, the challenges I faced, and how you can try it yourself.
The Problem: Flexbox Isn’t Always Enough
If you’ve built complex layouts in React Native, you should already know the pain and hassel involved in it. If you want a dashboard with cards that span multiple rows and columns, just prepare yourself for the battle of deeply nested View components, manual row management, and a lot of flex: 1 ratios.
Here’s what a “simple” grid layout looks like with traditional Flexbox:
// 😩 Flexbox: Nested Views, manual rows.
<View style={{ gap: 8 }}>
<View style={{ flexDirection: 'row', gap: 8 }}>
<View style={{ flex: 2 }}>Spans 2 colsView>
<View style={{ flex: 1 }}>Item 1View>
View>
<View style={{ flexDirection: 'row', gap: 8 }}>
<View style={{ flex: 2, gap: 8 }}>
<View style={{ flexDirection: 'row', gap: 8 }}>
<View style={{ flex: 1 }}>Item 2View>
<View style={{ flex: 1 }}>Item 3View>
View>
{/* More nesting for row-spanning... */}
View>
<View style={{ flex: 1 }}>Spans 2 rowsView>
View>
View>
Now imagine doing this for a real dashboard with 10+ widgets. It’s a nightmare to maintain.
The Dream: Native CSS Grid
What if we could just write this instead?
// ✨ CSS Grid: Flat structure, declarative placement
<View style={{
display: 'grid',
gridTemplateColumns: ['1fr', '1fr', '1fr'],
gap: 8,
}}>
<View style={{ gridColumnStart: 1, gridColumnEnd: 3 }}>Spans 2 colsView>
<View>Item 1View>
<View>Item 2View>
<View>Item 3View>
<View style={{ gridRowStart: 2, gridRowEnd: 4 }}>Spans 2 rowsView>
View>
No nesting. No manual row management. Items automatically flow into the grid. Row and column spanning is trivial.
This is now possible, thanks to an experimental patch from @intergalacticspacehighway that adds CSS Grid support directly to Yoga, React Native’s layout engine.
What I Built
I created a demo project to test and showcase this patch. It includes:
- A CSS Grid example screen demonstrating dashboards, 3-column grids, spanning items, and fractional units
- A Flexbox comparison screen showing the same layouts with traditional approaches
- Working builds on iOS and Android — this is native grid layout, not a web view hack
Tech Stack
| Package | Version |
|---|---|
| Expo SDK | 55 (Canary) |
| React Native | 0.83.0-rc.5 |
| React | 19.2.0 |
Yes, I’m living on the bleeding edge here. I chose to use React Native 0.83.0-rc.5 and Expo SDK 55 canary builds to try out the latest features alongside the grid patch.
How the Patch Works
The patch modifies React Native at multiple layers:
1. Yoga Layout Engine (The Core)
Yoga is the C++ layout engine that powers React Native’s UI. The patch adds:
-
New grid algorithm files:
GridLayout.cpp,AutoPlacement.h,TrackSizing.h -
Grid track types: Support for
frunits,auto, percentages, and fixed values -
Grid line positioning:
gridColumnStart,gridColumnEnd,gridRowStart,gridRowEnd -
A new display type:
YGDisplayGridalongsideYGDisplayFlexandYGDisplayNone
Here’s a snippet from the patch showing the new grid track list parsing:
// Grid Track List conversion - parses CSS grid track syntax
if (trackStr.size() > 2 && trackStr.substr(trackStr.size() - 2) == "fr") {
// Flex (fr) unit
float fr = std::stof(trackStr.substr(0, trackStr.size() - 2));
trackSize.minSizingFunction = yoga::StyleSizeLength::ofAuto();
trackSize.maxSizingFunction = yoga::StyleSizeLength::stretch(fr);
}
2. React Native StyleSheet Types
The patch extends TypeScript/Flow types to include grid properties:
display?: 'none' | 'flex' | 'contents' | 'grid' | undefined;
gridTemplateColumns?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
gridTemplateRows?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
gridColumnStart?: number | 'auto' | `span ${number}` | undefined;
gridColumnEnd?: number | 'auto' | `span ${number}` | undefined;
gridRowStart?: number | 'auto' | `span ${number}` | undefined;
gridRowEnd?: number | 'auto' | `span ${number}` | undefined;
3. Native Renderers (iOS & Android)
Both platforms needed updates to recognize the new grid display type and pass grid styles through to Yoga.
Supported Grid Properties
Container Properties
| Property | Example | Description |
|---|---|---|
display |
'grid' |
Enables grid layout |
gridTemplateColumns |
['1fr', '1fr', '1fr'] |
Defines column tracks |
gridTemplateRows |
['auto', 100, '1fr'] |
Defines row tracks |
gap |
16 |
Gap between grid items (same as flexbox) |
Item Properties
| Property | Example | Description |
|---|---|---|
gridColumnStart |
1 |
Column line to start at |
gridColumnEnd |
3 |
Column line to end at |
gridRowStart |
1 |
Row line to start at |
gridRowEnd |
4 |
Row line to end at |
Track Size Values
-
Numbers: Fixed pixels (e.g.,
100) -
'auto': Size based on content -
'Xfr': Fractional units (e.g.,'1fr','2fr') -
'X%': Percentage of container
Challenges I Faced
1. Manually Applying the Patch (The Hard Way)
Before I got the patch working, I tried manually applying all the changes by hand. I spent over 3 hours copying code, editing files, and cross-referencing the patch diff — only to hit build errors after everything was done.
Frustrated, I turned to AI agents for help. I went in circles with Claude Opus 4.5 and GPT 5.2, but they struggled to accurately make the changes across so many files. They’d run out of context window mid-task, lose track of what had been changed, and end up breaking things.
Thankfully, Gemini 3 Pro took a smarter approach: it downloaded the relevant code to a temporary file where it could reference the full context and make all the changes systematically. It worked on the first try. Sometimes the right tool makes all the difference.
2. TypeScript Type Conflicts
Expo’s react-native-web type definitions already include web-only grid properties (as strings). These conflicted with the new native grid types (which use arrays). I had to create a second patch to comment out the conflicting web types:
- gridTemplateColumns?: string;
+// gridTemplateColumns?: string;
This was a quick hack. A proper solution would involve conditional types or platform-specific type overrides.
3. Building from Source
Because the patch modifies native C++ code in Yoga, React Native must be built from source. This means:
// app.json
{
"expo": {
"plugins": [
["expo-build-properties", {
"ios": { "buildReactNativeFromSource": true },
"android": { "buildReactNativeFromSource": true }
}]
]
}
}
Initial builds take significantly longer (10-15 minutes on my M1 MacBook). Subsequent builds are faster thanks to caching, but it’s still a notable overhead.
4. Patch Application Strategy
I initially tried using patch-package, but ran into issues with the patch format and postinstall timing. I ended up using a simple postinstall script with the Unix patch command:
{
"postinstall": "patch -p1 < patches/react-native+0.83.0-rc.5.patch && patch -p1 < patches/expo+55.0.0-canary-20251212-acb11f2.patch"
}
5. Debugging Grid Layouts
When things didn’t work, debugging was challenging. The patch includes some printf statements for debugging track parsing:
printf("[GRID CONV] fromRawValue for GridTrackList calledn");
printf("[GRID CONV] tracks.size() = %zun", tracks.size());
These helped me verify that style values were being parsed correctly, but I had to rebuild native code each time I wanted to add more logging.
What Works (and What Doesn’t)
✅ Working
- Basic grid layouts with
gridTemplateColumnsandgridTemplateRows - Fractional units (
1fr,2fr) - Fixed pixel values and percentages
-
autosizing - Grid item placement with
gridColumnStart/EndandgridRowStart/End - Gap between items
- Auto-placement of items into the grid
- iOS and Android support
⚠️ Not Tested / Potentially Missing
-
grid-template-areas(named grid areas) -
grid-auto-flow(row vs column placement direction) -
minmax()function (partial support exists in the patch) - Subgrids
- Dense packing
Code Examples
Dashboard Layout
<View style={{
display: 'grid',
gridTemplateColumns: ['1fr', '1fr'],
gap: 16,
}}>
{/* Header - spans full width */}
<View style={{ gridColumnStart: 1, gridColumnEnd: 3 }}>
<Text>Dashboard HeaderText>
View>
{/* Stats cards - auto-placed into grid */}
<View><Text>Revenue: $12,875Text>View>
<View><Text>Users: 8,420Text>View>
{/* Chart - back to full width */}
<View style={{ gridColumnStart: 1, gridColumnEnd: 3 }}>
<Text>Activity ChartText>
View>
View>
Mixed Spanning
<View style={{
display: 'grid',
gridTemplateColumns: ['1fr', '1fr', '1fr'],
gap: 8,
}}>
<View style={{ gridColumnStart: 1, gridColumnEnd: 3 }}>
Spans 2 columns
View>
<View>1View>
<View>2View>
<View>3View>
<View style={{ gridRowStart: 2, gridRowEnd: 4, gridColumnStart: 3 }}>
Spans 2 rows
View>
<View>4View>
<View>5View>
View>
Try It Yourself
Prerequisites
- Node.js 18+
- pnpm
- Xcode (for iOS)
- Android Studio (for Android)
Setup
git clone https://github.com/OgDev-01/react-native-css-grid-patch
cd react-native-css-grid-patch
# Install dependencies (patches applied automatically)
pnpm install
# Generate native projects
pnpm run prebuild
# Run on iOS
pnpm run ios
# Run on Android
pnpm run android
The Future of Grid in React Native
This patch is experimental and not officially supported. However, it demonstrates that CSS Grid in React Native is technically feasible.
The fact that Yoga can be extended to support grid layout opens up exciting possibilities. If the community shows enough interest, we might see official grid support in a future React Native release.
“For now, if you’re willing to live on the edge and apply an experimental patch, you can start using CSS Grid in your React Native apps today.
Credits
- @intergalacticspacehighway for creating the CSS Grid patch
- The Yoga team for building an extensible layout engine
- The Expo team for making it relatively easy to build RN from source
Resources
Have you tried the CSS Grid patch? I’d love to hear about your experience. Find me on Twitter/X at https://x.com/OgDev_01 or open an issue on the repo!