Search code examples
accessibilityexporeact-native-web

How to have input with label in React Native Web (Expo)


Im using React Native Web in Expo. Ive read that it does accessibility properly however I can't see how to have a label for the input:

  <TextInput
    style={{height: 40, borderColor: 'gray', borderWidth: 1}}
    onChangeText={(text) => this.setState({text})}
    value={this.state.text}
  />

https://reactnative.dev/docs/0.53/textinput

For clarification and because it's weird name, I'm using React Native Web not React DOM:

https://github.com/necolas/react-native-web


Solution

  • You don't need to do anything special, you can just wrap the <textInput> in a label as you would with any input.

    Your code still outputs HTML at the end of the day.

    Something along the lines of (I don't know React so just use this is an example)

    import React, { Component } from 'react';
    import { AppRegistry, TextInput } from 'react-native';
    
    export default class UselessTextInput extends Component {
      constructor(props) {
        super(props);
        this.state = { text: 'Useless Placeholder' };
      }
    
      render() {
        return (
        <label>Text Label
          <TextInput
            style={{height: 40, borderColor: 'gray', borderWidth: 1}}
            onChangeText={(text) => this.setState({text})}
            value={this.state.text}
          /></label>
        );
      }
    }
    
    // skip this line if using Create React Native App
    AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput);
    

    As you are using React I presume you have no need to support Internet Explorer 8 and below so wrapping an input in a label will work correctly with all screen reader and browser combinations.

    If for any reason you do need to support the ancient stuff then you need to create an id on the input so you can use for="itemID" on the label. (the for on the label should contain the id of the input). Please note that you need to wrap them in a <div> if you do this due to the way React works.

    import React, { Component } from 'react';
    import { AppRegistry, TextInput } from 'react-native';
    
    export default class UselessTextInput extends Component {
      constructor(props) {
        super(props);
        this.state = { text: 'Useless Placeholder' };
      }
    
      render() {
        return (
        <div>
        <label for='itemID'>Text Label</label>
          <TextInput
            style={{height: 40, borderColor: 'gray', borderWidth: 1}}
            onChangeText={(text) => this.setState({text})}
            value={this.state.text}
            id="itemID"
          /></div>
        );
      }
    }
    
    // skip this line if using Create React Native App
    AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput);
    

    Finally you can add aria as you would any other property

    render() {
        return (
        <label>Text Label
          <TextInput
            style={{height: 40, borderColor: 'gray', borderWidth: 1}}
            onChangeText={(text) => this.setState({text})}
            value={this.state.text}
            aria-labelledby="id(s)OfElement(s)ToLabelThisInput"
          /></label>
        );
      }