feat: TextField component#4904
feat: TextField component#4904wonderlul wants to merge 1 commit intocallstack:@adrcotfas/feat/dynamic_themefrom
Conversation
|
Hey @wonderlul, thank you for your pull request 🤗. The documentation from this branch can be viewed here. |
There was a problem hiding this comment.
Below are some components described in the specs I see were not covered. Do we want them?
- in all error examples from the specs there's a trailing error icon
- the option to have leading and trailing text (called prefix and suffix) for currency, units of measurement etc; for suffix, the text field is aligned to the end instead of start;
- character counter (aligned to the end, same style as supporting text)
- The native filled
TextFieldhas an animation for the outline when selecting it: center to edges reveal - see https://m3.material.io/components/text-fields/guidelines
Some other notes:
- Please double check the tokens from the specs. I added some comments about opacity/color for certain elements that was missing; maybe there are other inconsistencies.
- A more complex example would be nice to cover more use cases and these elements, if we add them.
Here's a recording from this app showing the features mentioned above: https://github.com/material-components/material-components-android. I recommend installing this app to double check how TextField looks and behaves like in native.
screen-20260423-165915-1776952725646.mp4
|
|
||
| import { | ||
| ACCESORY_SIZE, | ||
| HELPER_FONT_SIZE, |
There was a problem hiding this comment.
For consistency with the specs, I would rename all occurrences of helper to supportingText and accessory to icon.
There was a problem hiding this comment.
MD3 describes the common case as leading/trailing icons, but the slots are intentionally generic: users may pass text, badges, custom controls, or non-icon content. Naming those props *Icon would misdescribe valid usage and nudge people toward icons only. “Accessory”-style naming is a deliberate pattern for flexible slots (similar to how many form libraries avoid over-specific names).
Agreed on supportingText — that matches the MD3 wording for the area under the field and keeps the API aligned with the spec without changing behavior. I’ll rename helper / related names to supportingText (and adjust docs/tests accordingly). Thanks for the suggestion.
There was a problem hiding this comment.
Sure, that makes sense but do we want to encourage users to add customization beyond what the guidelines describe like icons(icon buttons)?
The only place I found a non-icon accessory described was for the credit card example here, but we could argue that icon/image are equivalent: https://m3.material.io/components/text-fields/guidelines.
An image can help contextualize the required input text such as a credit card number.
For text there are different props we don't have yet in TextField: prefixText and suffixText which should be aligned with the actual text inside the text field, not centered vertically like the current accessories. See screen-20260423-165915-1776952725646.mp4.
In my opinion users of the library would appreciate a react-native-paper API as close as possible in naming with the MD3 specs (leadingIcon, trailingIcon) making it easier to follow the specs.
What do you think?
There was a problem hiding this comment.
I don’t have a strong opinion on this one — I think it’s really a product direction call: whether the public API should track MD3 as closely as possible (easier mental mapping from the spec) or stay a bit more generic so non–MD3 patterns stay natural in React Native.
Splitting things into leading/trailing icon plus prefix/suffix text (or similar) would line up better with the spec and can feel more “complete” but it also multiplies props and concepts for both authors and consumers, and we’d need clear rules for composition and order when several adornments exist. So it’s a tradeoff between spec fidelity and API surface / noise, not a purely technical right-or-wrong.
I’m fine either way the team wants to go; happy to adjust the implementation once there’s a preferred direction.
There was a problem hiding this comment.
I’m going to consider an alternative that accounts for all the possibilities and attempts to blend an MD3-first design with flexibility. I'll let you know soon.
3f47684 to
d0cffca
Compare
1c79eaf to
7e6e009
Compare
| // OPACITY | ||
| // ================== | ||
|
|
||
| export const FILLED_CONTAINER_ALPHA = 0.04; |
There was a problem hiding this comment.
Let's go with DISABLED_CONTAINER_OPACITY so we align with the specs token filled-text-field.disabled.container.opacity.
| // OPACITY | ||
| // ============ | ||
|
|
||
| export const OUTLINE_ALPHA = 0.12; |
There was a problem hiding this comment.
Let's go with DISABLED_OUTLINE_OPACITY to align with the specs outlined-text-field.disabled.outline.opacity
| paddingHorizontal: 0, | ||
| }; | ||
|
|
||
| /* Disabled tint (`onSurface @ 0.04`) is rendered as a childless overlay so its |
There was a problem hiding this comment.
This comment is duplicated in logic.ts and contains a magic number that might change. Perhaps keep the comment only here and mention the constant?
| $animatedActiveOutlineStyle?: Animated.WithAnimatedObject<ViewStyle>; | ||
| }; | ||
|
|
||
| export interface TextFieldProps extends TextInputProps { |
There was a problem hiding this comment.
It would be nice to support prefixText and suffixText aligned to the actual text field like in the specs instead of accessory/icon which is centered vertically. See screen-20260423-165915-1776952725646.mp4. For suffixText there should be a way to align the text in the text field at the end - something visible in the video example.
They use the same text style as the text in the textfield with onSurfaceVariant color for all states (isError, enabled, focused, unfocused) except for disabled where it's onSurfaceVariant at 0.38 opacity.
| TEXT_FIELD_ACCESSORY_MARGIN_HORIZONTAL + | ||
| TEXT_FIELD_INPUT_WRAPPER_PADDING_HORIZONTAL; | ||
|
|
||
| export const LABEL_LEFT_OFFSET_WITHOUT_ACCESSORY = |
There was a problem hiding this comment.
It would be nice to refactor all constants containing left/right to start/end to comply with RTL and MD naming.
| $animatedActiveOutlineStyle?: Animated.WithAnimatedObject<ViewStyle>; | ||
| }; | ||
|
|
||
| export interface TextFieldProps extends TextInputProps { |
There was a problem hiding this comment.
It would be nice to support the counterEnabled + counterMaxLength which lives together with supportingText aligned to the end.
BTW, counter is different from suffix but could serve the same purpose.
In the MD examples I see supportingText together with the counter in the same TextField. Exceeding the limit would not modify the supportingText but apply the error style to the counter:
screen-20260428-154624-1777380377501.mp4
| @@ -0,0 +1,213 @@ | |||
| import * as React from 'react'; | |||
There was a problem hiding this comment.
It would be nice to:
- improve the examples containing a trailing icon button to show a ripple when pressing it: now we don't have a visual feedback like in the
TextInputexamples - add other examples with counter, suffix/prefix text
- add any other examples you might find valuable equivalent to what we showcase in
TextInputsince we're replacing that component withTextField
Motivation
File structure
The component is split by variant (
filled/andoutlined/) and a root that wires shared behavior. Each area keeps logic, styles, utils, and constants in separate files. That follows patterns already used elsewhere in the library, but goes one step further so responsibilities stay obvious: variant-specific layout and theming do not drown in shared code, and the public API file stays focused on behavior and types.LeftAccessory/RightAccessoryvsTextInputadornmentsTextInputcomposes leading and trailing content throughleftandright, which are built around icons and affixes (TextInput.Icon,TextInput.Affix) and internal adornment types.TextFieldinstead exposesLeftAccessoryandRightAccessoryas render props (component types). The field passes curated layout and state—notably the merged style for alignment with the field, plus status, multiline, and editable—so accessories stay aligned with the input without re-implementing field internals. That supports arbitrary leading/trailing UI (clear actions, custom buttons, non-icon content) while still inheriting the important structural styles from the component.filled/outlinedinstead offlat/outlinedMaterial Design 3 describes text fields in terms of
filledandoutlinedstyles. The existing TextInput API uses mode:'flat' | 'outlined', where “flat” corresponds to the filled look. The new component names variants filled and outlined so the public API matches MD3 language and is easier to map from the spec and design tools.Style overrides
TextField is built as a small stack of clear layers, and each layer can be adjusted without fighting the rest. The outer pressable wrapper, the field shell (border, background, row that includes accessories), and the inner content wrapper (label + TextInput) each accept dedicated style props (pressableStyle, fieldStyle, containerStyle). The underlying TextInput still uses the normal style prop (and the rest of TextInputProps) for typography, padding, and input-specific layout. Label and helper text can be customized through labelProps and helperProps (including style). Leading/trailing UI uses LeftAccessory / RightAccessory, which receive a prepared style from the field so custom content stays aligned while remaining fully under your control. Together, this gives predictable “override the part you mean” behavior instead of a single opaque style that’s hard to reason about.
TextField instead of TextInput
Material Design 3 uses the term text field for this control. Exporting a second “Paper” input named
TextInputwould also blur the built-in React NativeTextInputin imports and documentation. A dedicatedTextFieldname keeps the design-system component clearly namespaced and aligned with MD3, while the underlying control remains React Native’sTextInputwhere appropriate.Positioning relative to TextInput
TextFieldis intended as the modern replacement path for form text entry in react-native-paper: implementation is structured for clarity and maintainability, and it adopts MD3-oriented theming (including use of thePlatformColor APIwhere it fits platform tokens). Compared to the legacyTextInputstack, this design aims to be easier to follow, less ad hoc in how variants and layout are split, and more efficient in how styles and state are applied—giving teams a refreshed, spec-aligned building block for new work without forcing an immediate break for existingTextInputusers.Order of merging
#4901
Related issue
#4878
#4329
#4235
Test plan
Run the Example app.
filledoutlined