Skip to content

[iOS] Fix force RTL support on new architecture.#49455

Closed
chrsmys wants to merge 1 commit into
react:mainfrom
chrsmys:nschris/RTLFix
Closed

[iOS] Fix force RTL support on new architecture.#49455
chrsmys wants to merge 1 commit into
react:mainfrom
chrsmys:nschris/RTLFix

Conversation

@chrsmys

@chrsmys chrsmys commented Feb 16, 2025

Copy link
Copy Markdown
Contributor

Summary

This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls _updateLayoutContext whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the SurfaceViews which were recreated on bundle reload.

Related Issues:

How can we take this further?

If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:

  • Make it so RCTI18nManager exports isRTL as a method instead of consts
  • Send Notification Center notif when RTL is forced on or off
  • Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

Test Plan:

On the new architecture change force the layout direction and reload the bundle:

import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

Simulator.Screen.Recording.-.iPhone.16.Plus.-.2025-02-16.at.14.05.30.mp4

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Feb 16, 2025
@cipolleschi

Copy link
Copy Markdown
Contributor

@NickGerleman can you have a look at this? From our internal discussions, I kind of understood that the fix should rely in Yoga/ShadowNode and not in the components' superclass, but you have more experience than me in this area.

@NickGerleman

Copy link
Copy Markdown
Contributor

This seems perfectly sane to me. I18nManager change should effect root constraint like this, which influences call to YGNodeCalculateLayout, instead of anything in the Yoga tree explicitly.

The followups to support this without reload also make sense! Fabric Android, last I tested, may actually do this already without bundle reload, but I'm not sure this behavior was explicit as much as emergent.

@facebook-github-bot

Copy link
Copy Markdown
Contributor

@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@a-eid

a-eid commented Feb 19, 2025

Copy link
Copy Markdown

@chrsmys Thank you for this contribution, I will patch my local RN & report back the results. also making RTL LTR direction changes without needing a bundle reload would be perfect. however it'll also require work for android.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Feb 19, 2025
@facebook-github-bot

Copy link
Copy Markdown
Contributor

@NickGerleman merged this pull request in 36f29be.

@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @chrsmys in 36f29be

When will my fix make it into a release? | How to file a pick request?

@PranavAvasthi

PranavAvasthi commented Mar 3, 2025

Copy link
Copy Markdown

@cipolleschi @chrsmys When will this be released?

@SalmanFaris53

Copy link
Copy Markdown

For me still its not working...drawermenu is not changing , bottom tab is not changing like from ltr to rtl

@PranavAvasthi

Copy link
Copy Markdown

@a-eid Have you tried this? If Yes, can you update that is it working or not?

@cipolleschi

Copy link
Copy Markdown
Contributor

We cut the branch for 0.79 yesterday. 0.79 will be out the week of the 7th of April, if nothing major happen.

If you want the fix to be backported, please open a pick request. Please open a different pick request for each version you'd like for the fix to be backported.

@ahmamedhat

Copy link
Copy Markdown

I tried this and its not working with bottom tabs

@a-eid

a-eid commented Mar 8, 2025

Copy link
Copy Markdown

@PranavAvasthi @ahmamedhat I have tried the fix and it seem to be working as expected.

@ahmamedhat

Copy link
Copy Markdown

@a-eid Please if you can tell me how did you try it? maybe I'm missing something

@a-eid

a-eid commented Mar 8, 2025

Copy link
Copy Markdown

@ahmamedhat I added the following patch, recompiled my project and it just worked.

diff --git a/React/Fabric/Surface/RCTFabricSurface.mm b/React/Fabric/Surface/RCTFabricSurface.mm
index 7b3ce5918ce5a159ef364cec030dd1dd00d587bb..3906df778bcd03038c9fd49fb52346cc252f2489 100644
--- a/React/Fabric/Surface/RCTFabricSurface.mm
+++ b/React/Fabric/Surface/RCTFabricSurface.mm
@@ -143,6 +143,7 @@ - (RCTSurfaceView *)view
 
   if (!_view) {
     _view = [[RCTSurfaceView alloc] initWithSurface:(RCTSurface *)self];
+    [self _updateLayoutContext];
     _touchHandler = [RCTSurfaceTouchHandler new];
     [_touchHandler attachToView:_view];
   }

react-native-bot pushed a commit that referenced this pull request Mar 10, 2025
Summary:
This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls  `_updateLayoutContext` whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the [SurfaceViews](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/Views/RCTRootShadowView.m#L18) which were recreated on bundle reload.

## Related Issues:
- react-native-community/discussions-and-proposals#847
- #49451
- #48311
- #45661

## How can we take this further?
If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:
- Make it so [RCTI18nManager](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/CoreModules/RCTI18nManager.mm#L52) exports isRTL as a method instead of consts
- Send Notification Center notif when RTL is forced on or off
- Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

## Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #49455

Test Plan:
On the new architecture change force the layout direction and reload the bundle:
```
import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

```

https://github.com/user-attachments/assets/0eab0d79-de3f-4eeb-abd0-439ba4fe25c0

Reviewed By: cortinico, cipolleschi

Differential Revision: D69797645

Pulled By: NickGerleman

fbshipit-source-id: 97499621f3dd735d466f5119e0f2a0eccf1c3c05
@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @chrsmys in 23b888f

When will my fix make it into a release? | How to file a pick request?

@1880akshay

Copy link
Copy Markdown

@cortinico when will the release (0.76.8 probably) happen with this fix?

@a-eid

a-eid commented Mar 21, 2025

Copy link
Copy Markdown

@chrsmys I am facing this weird issues sometimes where when I forceRTL to either true or false, I18nManager.isRTL doesn't update properly... however the layout does... any idea what might cause this ?

@chrsmys

chrsmys commented Mar 21, 2025

Copy link
Copy Markdown
Contributor Author

@chrsmys I am facing this weird issues sometimes where when I forceRTL to either true or false, I18nManager.isRTL doesn't update properly... however the layout does... any idea what might cause this ?

Are you reloading the bundle afterwards? At the moment that is a requirement. This weekend I may look into the additional fixes to make this unnecessary (Referenced in "How can we take this further?" section).

@a-eid

a-eid commented Mar 21, 2025

Copy link
Copy Markdown

@chrsmys Yes. Keep in mind that everything works fine after a reload. Elements that are laid out from left to right correctly switch to right to left. However, I18nManager.isRTL does not update, which causes issues with elements like arrows and the iOS navigation transition direction.

@chrsmys

chrsmys commented Mar 21, 2025

Copy link
Copy Markdown
Contributor Author

Can you file a new issue with a small repro project? I can take a look this weekend.

@SalmanFaris53

SalmanFaris53 commented Mar 22, 2025 via email

Copy link
Copy Markdown

@a-eid

a-eid commented Mar 22, 2025

Copy link
Copy Markdown

@SalmanFaris53 that's because arrows & drawers direction is depending on I18nManager.isRTL boolean flag, which doesn't update some times.

@a-eid

a-eid commented Mar 26, 2025

Copy link
Copy Markdown

@chrsmys I've created a repro, I am working on my STR, cuz it doesn't always happen.

cc @SalmanFaris53 let me know if you're able to help.

@KarinaOliinykCommandpost

Copy link
Copy Markdown

I also have this issue on iOS

@baraa-rasheed

Copy link
Copy Markdown

@a-eid The UI is being updated correctly but the native-stack navigator is not, you can test this by setting the headerLargeTitle to true and try to change the RTL direction.

@a-eid

a-eid commented Apr 10, 2025

Copy link
Copy Markdown

@baraa-rasheed everything is updating for me just fine, however sometimes I18nManager.isRTL doesn't update correct. which affects the navigation direction. not sure why. hopefully @chrsmys will have an update soon.

@cipolleschi

Copy link
Copy Markdown
Contributor

Stack navigator is part of react-avigation, is it perhaps worth opening an issue in the react-navigation repository.

@fawwazAlkarmy

Copy link
Copy Markdown

I have still an issues with RTL on IOS , i'm using
"expo": "^52.0.44",
"react-native": "0.76.9",

on first run the layout isn't correct , after i reload it works fine the layout changes , however not all elements works correctly, for example the same Text component works correctly in some places and doesn't in others, i don't know why i have this weird issue till now, any help will be appreciated.

react-native-bot pushed a commit that referenced this pull request Jul 1, 2025
Summary:
This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls  `_updateLayoutContext` whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the [SurfaceViews](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/Views/RCTRootShadowView.m#L18) which were recreated on bundle reload.

## Related Issues:
- react-native-community/discussions-and-proposals#847
- #49451
- #48311
- #45661

## How can we take this further?
If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:
- Make it so [RCTI18nManager](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/CoreModules/RCTI18nManager.mm#L52) exports isRTL as a method instead of consts
- Send Notification Center notif when RTL is forced on or off
- Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

## Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #49455

Test Plan:
On the new architecture change force the layout direction and reload the bundle:
```
import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

```

https://github.com/user-attachments/assets/0eab0d79-de3f-4eeb-abd0-439ba4fe25c0

Reviewed By: cortinico, cipolleschi

Differential Revision: D69797645

Pulled By: NickGerleman

fbshipit-source-id: 97499621f3dd735d466f5119e0f2a0eccf1c3c05
@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @chrsmys in bf17c3b

When will my fix make it into a release? | How to file a pick request?

@a-eid

a-eid commented Jul 1, 2025

Copy link
Copy Markdown

it's probably worth noting that, not all reloads change the direction correctly on IOS. this probably needs investigation since the new arch is the default now and the old arch is frozen.

fabriziocucci pushed a commit that referenced this pull request Jul 9, 2025
Summary:
This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls  `_updateLayoutContext` whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the [SurfaceViews](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/Views/RCTRootShadowView.m#L18) which were recreated on bundle reload.

## Related Issues:
- react-native-community/discussions-and-proposals#847
- #49451
- #48311
- #45661

## How can we take this further?
If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:
- Make it so [RCTI18nManager](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/CoreModules/RCTI18nManager.mm#L52) exports isRTL as a method instead of consts
- Send Notification Center notif when RTL is forced on or off
- Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

## Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #49455

Test Plan:
On the new architecture change force the layout direction and reload the bundle:
```
import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

```

https://github.com/user-attachments/assets/0eab0d79-de3f-4eeb-abd0-439ba4fe25c0

Reviewed By: cortinico, cipolleschi

Differential Revision: D69797645

Pulled By: NickGerleman

fbshipit-source-id: 97499621f3dd735d466f5119e0f2a0eccf1c3c05
@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @chrsmys in 1a1814a

When will my fix make it into a release? | How to file a pick request?

@a-eid

a-eid commented Oct 7, 2025

Copy link
Copy Markdown

@chrsmys did you get a chance to take another look at this? It still doesn’t work 100% of the time — sometimes when we force RTL or LTR and reload the bundle, the app keeps the previous direction until it’s completely terminated and restarted.

@sanataff

sanataff commented Oct 7, 2025

Copy link
Copy Markdown

@a-eid hello, I am having the same issue, I am unable to switch between RTL and LTR in my app without restarting, is their currently any work around for this issue ?

@saleh2001k

Copy link
Copy Markdown
Screen.Recording.2025-11-19.at.1.mp4

Hello everyone,

I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app.
To achieve this, you need to rely on
@react-navigation/stack
instead of
@react-navigation/native.

I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.

You can follow the kit here:
https://github.com/saleh2001k/native-tide-starter-kit

@a-eid

a-eid commented Nov 19, 2025

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,

I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.

I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.

You can follow the kit here: saleh2001k/native-tide-starter-kit

are you relaying on the stack direction property ? and text align right / left

@sanataff

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,

I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.

I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.

You can follow the kit here: https://github.com/saleh2001k/native-tide-starter-kit

This is really interesting, haven't looked deeply yet but if you need any help I would like to contribute

@saleh2001k

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: saleh2001k/native-tide-starter-kit

are you relaying on the stack direction property ? and text align right / left

in some ways, yes, for best practice, I recommend to creat a Text component and use it instead of using Text from react native directly. I will provide some more screens and ui components soon in the kit, for the stack the core feature is to use gestureDirection, it handles this case like true rtl when using native stack

@saleh2001k

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: https://github.com/saleh2001k/native-tide-starter-kit

This is really interesting, haven't looked deeply yet but if you need any help I would like to contribute

That's sound great, the kit is still in a very early version, and I have so many todos in it, you can reach me out for more information or future updates

@sanataff

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: https://github.com/saleh2001k/native-tide-starter-kit

This is really interesting, haven't looked deeply yet but if you need any help I would like to contribute

That's sound great, the kit is still in a very early version, and I have so many todos in it, you can reach me out for more information or future updates

what is your preferred communication channel ?

@saleh2001k

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: https://github.com/saleh2001k/native-tide-starter-kit

This is really interesting, haven't looked deeply yet but if you need any help I would like to contribute

That's sound great, the kit is still in a very early version, and I have so many todos in it, you can reach me out for more information or future updates

what is your preferred communication channel ?

You can reach me on Discord: salehayman, or my email: saleh.almashnie@gmail.com. If you prefer another platform, we can do it. I'm available on most platforms

@a-eid

a-eid commented Nov 19, 2025

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: saleh2001k/native-tide-starter-kit

are you relaying on the stack direction property ? and text align right / left

in some ways, yes, for best practice, I recommend to creat a Text component and use it instead of using Text from react native directly. I will provide some more screens and ui components soon in the kit, for the stack the core feature is to use gestureDirection, it handles this case like true rtl when using native stack

this probably is not the best idea. this needs to be done on the native layer.

@saleh2001k

Copy link
Copy Markdown

Screen.Recording.2025-11-19.at.1.mp4
Hello everyone,
I’ve managed to create a true LTR ↔ RTL flow without needing to refresh the app. To achieve this, you need to rely on @react-navigation/stack instead of @react-navigation/native.
I’m currently working on a starter kit that will support this feature and will also include improvements to make both Stack and Native navigation work more smoothly.
You can follow the kit here: saleh2001k/native-tide-starter-kit

are you relaying on the stack direction property ? and text align right / left

in some ways, yes, for best practice, I recommend to creat a Text component and use it instead of using Text from react native directly. I will provide some more screens and ui components soon in the kit, for the stack the core feature is to use gestureDirection, it handles this case like true rtl when using native stack

this probably is not the best idea. this needs to be done on the native layer.

I agree with you, but in order for it to work in the native layer, you must refresh the app, and it's not an easy task to handle it fully native now, the current approach is very stable for the issues that I'm facing, and it might help some people until it's fixed.

@saleh2001k

Copy link
Copy Markdown

Update: I created a package that solves the layout when switching from ltr to rtl, or the opposite, without restarting the app, with gesture mirrored, all at runtime, no state loss

Screen.Recording.2026-07-08.at.7.36.34.PM-small.mp4

Check it out here: https://github.com/saleh2001k/react-native-rtl#readme

It's still in an early stage, but I think there's a great potential to fix this issue once and for all, new arch only, tested in rn 0.81.4, rn 0.85.2, and expo 54

All native, works on @react-navigation/native-stack, no JS needs or conditional render based on the lang, same as restarting flow but without restarting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.