<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Jackson</title>
    <description>The latest articles on DEV Community by Alex Jackson (@alexjjackson).</description>
    <link>https://dev.to/alexjjackson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3971211%2F05fb3649-3dd7-4159-a6a8-4f8df936e0c3.png</url>
      <title>DEV Community: Alex Jackson</title>
      <link>https://dev.to/alexjjackson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexjjackson"/>
    <language>en</language>
    <item>
      <title>Accessible Forms in React Native: A Complete Reference Guide</title>
      <dc:creator>Alex Jackson</dc:creator>
      <pubDate>Sat, 06 Jun 2026 15:25:20 +0000</pubDate>
      <link>https://dev.to/alexjjackson/accessible-forms-in-react-native-a-complete-reference-guide-115o</link>
      <guid>https://dev.to/alexjjackson/accessible-forms-in-react-native-a-complete-reference-guide-115o</guid>
      <description>&lt;p&gt;Forms are everywhere in mobile apps - authentication flows, data entry, support requests, onboarding... If your app has a login screen, a form is likely the first thing a new user interacts with. That makes accessibility here not just a nice-to-have, but a first impression.&lt;/p&gt;

&lt;p&gt;The problem is that forms are consistently one of the most broken areas for assistive technology users. Missing labels, keyboard traps, silent validation errors, focus going nowhere after submission - these are issues that make an app unusable for a significant portion of your users.&lt;/p&gt;

&lt;p&gt;This guide is a complete reference for building forms that work for everyone in React Native, whether users are navigating with their fingers, an external keyboard, a screen reader or voice input. Code examples throughout show both &lt;strong&gt;what to do&lt;/strong&gt; and &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A fully working demo repo is available to fork and test on a real device - check it out at &lt;a href="https://github.com/AlexJackson01/accessible-form-demo" rel="noopener noreferrer"&gt;rn-accessible-form-demo&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Labels
&lt;/h2&gt;

&lt;p&gt;Every form field needs a label, whether a text input, checkbox or radio/submit buttons. No exceptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't rely on placeholders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Placeholder text disappears the moment a user starts typing. Screen readers will read it initially, but once it's gone, there's no way for them to recall what the field was for without clearing their input. Placeholders are useful as hints, not as labels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use a visual label + accessibilityLabel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To avoid screen readers announcing the same information twice (once for the visual label, once for the input), hide the visual label from assistive technology and put the full label on the input itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;
  &lt;span class="na"&gt;importantForAccessibility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityElementsHidden&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Email address*
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email address, required"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;importantForAccessibility="no"&lt;/code&gt; handles Android, and &lt;code&gt;accessibilityElementsHidden&lt;/code&gt; handles iOS. Together they tell assistive technology to skip the visual label entirely - the &lt;code&gt;accessibilityLabel&lt;/code&gt; on the &lt;code&gt;TextInput&lt;/code&gt; is the single source of truth for screen readers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required fields&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Declare required fields within the &lt;code&gt;accessibilityLabel&lt;/code&gt; - not just with a visual asterisk. A screen reader user has no way of knowing what an asterisk means unless you tell them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Screen reader reads "Email address" — no indication it's required&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt; &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email address"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Screen reader reads "Email address, required"&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt; &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email address, required"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use accessibilityHint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;accessibilityHint&lt;/code&gt; for additional context that doesn't belong in the label - format guidance, what the field is used for or what happens after interaction. It's read after the label and role, and users can disable hints in their accessibility settings, so never put critical information here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Date of birth, required"&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityHint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Enter in DD/MM/YYYY format"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Headings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;accessibilityRole="header"&lt;/code&gt; on form titles and section headings. Screen reader users can navigate by heading to jump quickly to sections, and it's read aloud as a heading rather than plain text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Contact Us
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Radio Buttons and Predefined Options
&lt;/h2&gt;

&lt;p&gt;When information can be selected from a known set of options, radio buttons are more accessible than a free text input. Users can see all available choices at a glance and select without typing, which reduces errors and cognitive load, particularly for users navigating by screen reader or keyboard.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;accessibilityRole="radio"&lt;/code&gt; on each option and &lt;code&gt;accessibilityRole="radiogroup"&lt;/code&gt; on the container. The group tells screen readers the options are related. &lt;code&gt;accessibilityState={{ selected }}&lt;/code&gt; reflects the selected state so screen readers announce "selected" or "not selected" alongside the label.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radiogroup"&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Preferred contact method, required"&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityState&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
    &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* custom styled radio UI */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityState&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;phone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Phone"&lt;/span&gt;
    &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;phone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* custom styled radio UI */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Error handling follows the same pattern as text inputs - embed the error in the group's &lt;code&gt;accessibilityLabel&lt;/code&gt; and move focus to the group container. More on this below.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checkboxes and Legal Consent
&lt;/h2&gt;

&lt;p&gt;For any action with legal or financial implications - agreeing to terms and conditions, signing up to a subscription, confirming a payment - always present the option as an explicit checkbox. Users should make a deliberate, informed choice rather than having consent assumed.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;accessibilityRole="checkbox"&lt;/code&gt; and &lt;code&gt;accessibilityState={{ checked }}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityState&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I agree to the Terms and Conditions, required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityHint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"You must agree to the Terms and Conditions before submitting"&lt;/span&gt;
  &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* custom styled checkbox UI */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TouchableOpacity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the checkbox is required and unchecked on submission, the error follows the same focus and label pattern as other fields as detailed below - move focus to the checkbox and embed the error in the &lt;code&gt;accessibilityLabel&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keyboard Behaviour
&lt;/h2&gt;

&lt;p&gt;Users should be able to navigate through your entire form using the return key on their on-screen keyboard, without getting stuck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set the correct keyboard type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Match the keyboard to the expected input. This reduces cognitive load and prevents errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Phone number"&lt;/span&gt;
  &lt;span class="na"&gt;keyboardType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone-pad"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common values: &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;email-address&lt;/code&gt;, &lt;code&gt;numeric&lt;/code&gt;, &lt;code&gt;phone-pad&lt;/code&gt;, &lt;code&gt;decimal-pad&lt;/code&gt;. Full list in the &lt;a href="https://reactnative.dev/docs/textinput#keyboardtype" rel="noopener noreferrer"&gt;React Native docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;returnKeyType&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;returnKeyType="next"&lt;/code&gt; on every field except the last, and &lt;code&gt;returnKeyType="done"&lt;/code&gt; on the final field. This tells users where they are in the form and what pressing return will do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email address, required"&lt;/span&gt;
  &lt;span class="na"&gt;returnKeyType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Phone number"&lt;/span&gt;
  &lt;span class="na"&gt;keyboardType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone-pad"&lt;/span&gt;
  &lt;span class="na"&gt;returnKeyType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chain focus between fields&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a user presses the return key, focus should move to the next field automatically. Use &lt;code&gt;onSubmitEditing&lt;/code&gt;, &lt;code&gt;submitBehaviour&lt;/code&gt; and refs to wire this up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phoneInputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email address, required"&lt;/span&gt;
  &lt;span class="na"&gt;returnKeyType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt;
  &lt;span class="na"&gt;onSubmitEditing&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;phoneInputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;phoneInputRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Phone number"&lt;/span&gt;
  &lt;span class="na"&gt;keyboardType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"phone-pad"&lt;/span&gt;
  &lt;span class="na"&gt;returnKeyType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;
  &lt;span class="na"&gt;onSubmitEditing&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submitForm&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;submitBehavior&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'blurAndSubmit'&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user reaches the final field and presses 'Done' or their return key, &lt;code&gt;onSubmitEditing&lt;/code&gt; fires &lt;code&gt;submitForm&lt;/code&gt; so the form can be submitted entirely from the keyboard without needing to reach for a button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ iOS gotcha: numeric keyboards and onSubmitEditing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your final field uses &lt;code&gt;keyboardType="phone-pad"&lt;/code&gt; on iOS, &lt;code&gt;onSubmitEditing&lt;/code&gt; will not fire when the user taps 'Done'. This is an iOS platform limitation. Make sure your submit button is clearly reachable after dismissing the keyboard, and consider adding a toolbar above the keyboard with a 'Submit' action.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation Errors
&lt;/h2&gt;

&lt;p&gt;This is where most forms fall apart. Getting validation right is the difference between a form that's usable by screen reader users and one that isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field-level errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When validation fails, the first field with an error should receive focus automatically. The error message should be part of that field's &lt;code&gt;accessibilityLabel&lt;/code&gt; so it's read out immediately - users shouldn't need to navigate to a separate error element to understand what went wrong.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailInputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;emailInputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
  &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;emailInputRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Email address, required, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;emailErrorText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;returnKeyType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt;
  &lt;span class="na"&gt;onSubmitEditing&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;phoneInputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the error appears, focus moves to the email input and VoiceOver/TalkBack reads: &lt;em&gt;"Email address, required, Please enter a valid email address"&lt;/em&gt; or your equivalent email error message.&lt;/p&gt;

&lt;p&gt;The visual error message should still render on screen for sighted users, but hide it from assistive technology to avoid it being read twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="na"&gt;importantForAccessibility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt; &lt;span class="na"&gt;accessibilityElementsHidden&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;emailErrorText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multiple field errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If multiple fields fail validation simultaneously, focus the first field with an error. Use a &lt;code&gt;useEffect&lt;/code&gt; that watches all error states and prioritises accordingly. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;emailInputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showPhoneError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;phoneInputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showEmailError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;showPhoneError&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Submission errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For errors that aren't tied to a specific field, e.g. network failures, server errors or unexpected problems, use &lt;code&gt;AccessibilityInfo.announceForAccessibility&lt;/code&gt; to read the message out immediately. This is a live announcement that interrupts the current screen reader output, so the user knows something went wrong without needing to navigate anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;submitForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendFormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setSubmissionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;announceForAccessibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Display the error visually above the submit button so sighted users can see it. Since &lt;code&gt;announceForAccessibility&lt;/code&gt; already handles screen readers, you can leave it visible to all - there's no double-reading issue here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submissionError&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submissionErrorText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Success State and Focus Management
&lt;/h2&gt;

&lt;p&gt;What happens after a form submits successfully is just as important as what happens during validation. If focus stays on the submit button or disappears entirely, screen reader users have no way of knowing the form worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success screen&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cleanest approach is to replace the form entirely on success - no ambiguity about what happened. Render a success message and move focus to the heading automatically using &lt;code&gt;AccessibilityInfo.setAccessibilityFocus&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;successRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;submitted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reactTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;_nativeTag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reactTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAccessibilityFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reactTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;submitted&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The small delay gives the success screen time to render before focus is moved - without it, the element may not yet exist in the native view hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;accessible prop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;setAccessibilityFocus&lt;/code&gt; to work on a &lt;code&gt;Text&lt;/code&gt; element, it must be marked as &lt;code&gt;accessible&lt;/code&gt;. Without this, screen readers can't receive focus on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;
  &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;successRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;accessible&lt;/span&gt;
  &lt;span class="na"&gt;accessibilityRole&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Form submitted!
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When focus moves to this element, VoiceOver reads: &lt;em&gt;"Form submitted!, heading."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Toasts and timed notifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A common alternative to a full success screen is a toast - a brief notification that appears on screen and disappears after a few seconds. They're quick to implement and feel lightweight, but they come with real accessibility tradeoffs worth understanding before reaching for them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The issue&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A toast that appears and disappears visually gives sighted users a moment to read it. For screen reader users, that same toast may never be announced at all or announced mid-navigation, interrupt the wrong thing, or disappear before the user has finished processing it. Timed components that vanish without warning are a WCAG failure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Making toasts accessible&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The minimum requirement is that the content is announced to screen readers immediately when it appears, regardless of whether the user navigates to it. Use &lt;code&gt;AccessibilityInfo.announceForAccessibility&lt;/code&gt; to read the message out loud at the moment the toast mounts - the same pattern used above for submission errors. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toastVisible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;announceForAccessibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toastMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;toastVisible&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures screen reader users hear the message even if the toast disappears before they navigate to it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Timing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a toast auto-dismisses, the duration needs to be long enough for users to read it - WCAG recommends at least five seconds for short messages and longer for anything more complex. Where possible, give users a way to dismiss the toast manually rather than relying on a timer alone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When to use&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Toasts are appropriate for low-stakes, non-critical feedback, e.g. "Saved", "Copied to clipboard", "Message sent". They are not appropriate for errors, warnings, or anything the user needs to act on - these should use the focus-based patterns covered earlier in this guide.&lt;/p&gt;

&lt;p&gt;For form submission success specifically, a full success screen with focus management is the more accessible choice. A toast is a reasonable fallback if the form remains visible after submission (for example, a search form or a filter), but the message must still be announced via &lt;code&gt;announceForAccessibility&lt;/code&gt; - it cannot rely on the user navigating to it before it disappears.&lt;/p&gt;




&lt;h2&gt;
  
  
  Review, Undo and Reversible Actions
&lt;/h2&gt;

&lt;p&gt;Before a form submission is final - particularly one with legal or financial implications - users should have an opportunity to review what they've entered and reverse the action if needed.&lt;/p&gt;

&lt;p&gt;This matters for all users, but especially for screen reader users who navigate sequentially and may not have a complete picture of everything they've filled in. A confirmation step, a summary screen, or a clear cancellation path gives everyone the chance to catch mistakes before they matter.&lt;/p&gt;

&lt;p&gt;Practical patterns to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirmation step&lt;/strong&gt; - for high-stakes actions (subscribing to a service, making a payment, deleting an account), show a summary of what the user is about to do and ask them to confirm before completing the action;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cancellation&lt;/strong&gt; - make it easy to cancel or go back at any point in a multi-step form and ensure the cancel action is reachable by keyboard and screen reader;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Destructive actions&lt;/strong&gt; - if a form action is irreversible (closing an account, submitting a legal document), say so explicitly in the UI - not just in the terms - so users understand what they're agreeing to before they confirm;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Session persistence&lt;/strong&gt; - if a user navigates away from a long form, consider persisting their input so they don't lose work. Losing form data is frustrating for any user; for someone navigating with assistive technology it can mean starting a lengthy process from scratch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The success screen in the demo form demonstrates the lightest version of this - the user can tap "Submit another response" to go back and the form resets cleanly. For real subscription or payment flows, a dedicated review screen before final submission is the right pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Voice input
&lt;/h2&gt;

&lt;p&gt;If you've followed the patterns above, your form will work with voice input without any additional changes. The &lt;code&gt;accessibilityLabel&lt;/code&gt; values are what voice control software (iOS Voice Control, Android Voice Access) uses to identify fields - clear, descriptive labels mean users can say "tap Email address" and the right field activates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To access voice input:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iOS&lt;/strong&gt; - Settings → Accessibility → Voice Control → enable. Tap the microphone icon in any text field to dictate&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android&lt;/strong&gt; - Download Gboard, enable Google voice typing and tap the microphone icon in any input&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing your form
&lt;/h2&gt;

&lt;p&gt;Building accessible forms and testing accessible forms are two different things. Always verify with real assistive technology on a real device - automated tools won't catch focus management or announcement order issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VoiceOver (iOS)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Settings → Accessibility → VoiceOver → enable&lt;/li&gt;
&lt;li&gt;Swipe right to navigate forward through elements, left to go back&lt;/li&gt;
&lt;li&gt;Double-tap to activate&lt;/li&gt;
&lt;li&gt;Navigate through the entire form: are all fields announced correctly? Are errors read out and fields focused as expected? Does focus move to the success heading on submission?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TalkBack (Android)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Settings → Accessibility → TalkBack → enable&lt;/li&gt;
&lt;li&gt;Swipe right/left to navigate, double-tap to activate&lt;/li&gt;
&lt;li&gt;Check the same things as VoiceOver - but also verify that &lt;code&gt;importantForAccessibility="no"&lt;/code&gt; is working correctly, as Android and iOS handle this differently in some edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;External keyboard&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect a Bluetooth keyboard and navigate using Tab and Return only&lt;/li&gt;
&lt;li&gt;You should never get trapped in a field and focus should always be visible&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every field has an &lt;code&gt;accessibilityLabel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Required fields say "required" in the label&lt;/li&gt;
&lt;li&gt;Form title uses &lt;code&gt;accessibilityRole="header"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Predefined options use radio buttons rather than free text where appropriate&lt;/li&gt;
&lt;li&gt;Radio groups use &lt;code&gt;accessibilityRole="radiogroup"&lt;/code&gt; on the container&lt;/li&gt;
&lt;li&gt;Each radio option uses &lt;code&gt;accessibilityRole="radio"&lt;/code&gt; and &lt;code&gt;accessibilityState={{ selected }}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Legal consent uses an explicit checkbox with &lt;code&gt;accessibilityRole="checkbox"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Checkbox state is declared explicitly in the accessibilityLabel as "checked" or "not checked"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;returnKeyType&lt;/code&gt; is set correctly on all fields&lt;/li&gt;
&lt;li&gt;Focus moves between fields via onSubmitEditing&lt;/li&gt;
&lt;li&gt;The iOS numeric keyboard gotcha is accounted for if relevant&lt;/li&gt;
&lt;li&gt;Validation errors auto-focus the first errored field&lt;/li&gt;
&lt;li&gt;Error text is included in the field's &lt;code&gt;accessibilityLabel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Visual error messages are hidden from assistive technology&lt;/li&gt;
&lt;li&gt;Submission errors use &lt;code&gt;announceForAccessibility&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Success state moves focus to a confirmation heading&lt;/li&gt;
&lt;li&gt;Toasts announce their content via &lt;code&gt;announceForAccessibility&lt;/code&gt; when they appear, if necessary to use, and are visible for long enough to read&lt;/li&gt;
&lt;li&gt;Toasts are not used for errors or actions that require user attention&lt;/li&gt;
&lt;li&gt;High-stakes or irreversible actions have a confirmation or review step&lt;/li&gt;
&lt;li&gt;Users can cancel or go back at any point in a multi-step form&lt;/li&gt;
&lt;li&gt;Destructive or legal actions describe their consequences clearly in the UI&lt;/li&gt;
&lt;li&gt;Tested with VoiceOver / TalkBack or voice input on real devices, as well as keyboards&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Accessible forms aren't significantly more work than inaccessible ones, but the patterns need to be deliberate from the start. Retrofitting accessibility into a form that wasn't designed for it is much harder than building it in correctly the first time.&lt;/p&gt;

&lt;p&gt;The demo repo has a fully working implementation of everything in this guide - fork it, run it on a real device and break things deliberately to see what happens. &lt;a href="https://github.com/AlexJackson01/accessible-form-demo" rel="noopener noreferrer"&gt;rn-accessible-form-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, have patterns to add or would like advice on building your own accessible forms, find me on &lt;a href="https://www.linkedin.com/in/alex-j-jackson/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and let's chat.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>a11y</category>
      <category>mobile</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
