Skip to content

fix(ios): align text to the right by default in RTL#57634

Open
zoontek wants to merge 1 commit into
react:mainfrom
zoontek:fix-ios-rtl-text
Open

fix(ios): align text to the right by default in RTL#57634
zoontek wants to merge 1 commit into
react:mainfrom
zoontek:fix-ios-rtl-text

Conversation

@zoontek

@zoontek zoontek commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary:

On the new architecture, <Text> without an explicit textAlign behaves differently across platforms when the layout direction is RTL:

  • Android: text aligns to the right (follows the layout direction).
  • iOS: text stays left-aligned (ignores the layout direction).

This is a regression from the old architecture, where both platforms left text alone when textAlign was unset. The new arch changed Android's default, and the two platforms are now inconsistent.

This PR makes iOS match the current Android (new arch) behavior: without an explicit textAlign, text follows the layout direction and aligns to the right in RTL. On iOS, Natural alignment now resolves to Right under RTL.

Open question

I aligned iOS to Android's new default, but I'm not sure it's the right call (my guts feeling think that is the right thing to do, tho). The old-arch behavior (do nothing when textAlign is unset) may be what's actually expected, in which case the fix should go the other way: revert Android to leave text untouched and keep iOS as-is. Happy to flip the direction if reviewers prefer that.

Changelog:

[IOS] [FIXED] - Align text to the right by default in RTL when textAlign is not set, matching Android

Test Plan:

Edit RNTesterAppShared.js, press the button, reload the app - comment / uncomment Text textAlign style:

/**
 * @flow
 * @format
 */

import {
  I18nManager,
  SafeAreaView,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

I18nManager.allowRTL(true);
I18nManager.swapLeftAndRightInRTL(true);

export default function App(): React.Node {
  const isRTL = I18nManager.isRTL;

  return (
    <View style={styles.wrapper}>
      <SafeAreaView style={styles.content}>
        <Text style={styles.info}>I18nManager.isRTL: {String(isRTL)}</Text>

        <TouchableOpacity
          onPress={() => I18nManager.forceRTL(!isRTL)}
          style={styles.button}>
          <Text style={styles.buttonText}>Toggle RTL + reload manually</Text>
        </TouchableOpacity>

        <View style={styles.row}>
          <View style={[styles.box, {backgroundColor: '#FF6B6B'}]}>
            <Text style={styles.boxText}>1</Text>
          </View>
          <View style={[styles.box, {backgroundColor: '#4ECDC4'}]}>
            <Text style={styles.boxText}>2</Text>
          </View>
          <View style={[styles.box, {backgroundColor: '#45B7D1'}]}>
            <Text style={styles.boxText}>3</Text>
          </View>
        </View>

        <Text
        // style={{textAlign: 'left'}} // COMMENT / UNCOMMENT THIS
        >
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua.
        </Text>
      </SafeAreaView>
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: {
    backgroundColor: 'white',
    flex: 1,
  },
  content: {
    gap: 20,
    marginHorizontal: 20,
  },
  info: {
    fontSize: 18,
    fontWeight: 'bold',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
  },
  buttonText: {
    color: 'white',
    textAlign: 'center',
    fontWeight: '600',
  },
  row: {
    flexDirection: 'row',
    justifyContent: 'space-around',
  },
  box: {
    width: 60,
    height: 60,
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: 8,
  },
  boxText: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
  },
});

Old architecture (baseline):

Android iOS
android - old arch ios - old arch

New architecture (before this PR, iOS diverges):

Android iOS
android - new arch ios - new arch - not fixed

New architecture (after this PR, iOS fixed):

Android iOS
android - new arch ios - new arch - fixed

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 22, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 22, 2026
@meta-codesync

meta-codesync Bot commented Jul 22, 2026

Copy link
Copy Markdown

@Abbondanzo has imported this pull request. If you are a Meta employee, you can view this in D113244783.

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. p: Expo Partner: Expo Partner 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.

1 participant