Uh oh!
There was an error while loading. Please reload this page.
Adds character counts to TextArea and TextInput components - #7293
Conversation
🦋 Changeset detectedLatest commit: dbf0f21 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the |
| {...rest} | ||
| aria-describedby={ | ||
| characterLimit | ||
| ? [characterCountStaticMessageId, rest['aria-describedby']].filter(Boolean).join(' ') || undefined |
There was a problem hiding this comment.
We needed to add the characterCountStaticMessageId as an aria-describedby ID to ensure that the user is getting the information about the character limit.
| <VisuallyHidden id={characterCountLiveRegionId} aria-live="polite" role="status"> | ||
| {screenReaderMessage} | ||
| </VisuallyHidden> | ||
| <VisuallyHidden id={characterCountStaticMessageId}> |
There was a problem hiding this comment.
This is hidden as we want to include it for screen reader users, but including the actual character counter span that updates when a user types was causing double announcements.
| <UnstyledTextInput | ||
| // @ts-expect-error it needs a non nullable ref | ||
| ref={inputRef} | ||
| <> |
There was a problem hiding this comment.
I just wrapped the <TextInputWrapper> in an empty <> since we're adding another sibling component. I'll call out specific changes within this part of the diff!
siddharthkp
left a comment
There was a problem hiding this comment.
Trust you with the implementation overall. Left a couple small nits.
Next steps:
- Add e2e tests + add
update snapshotslabel - Run integration tests
Uh oh!
There was an error while loading. Please reload this page.
| const [isInputFocused, setIsInputFocused] = useState<boolean>(false) | ||
| const inputRef = useProvidedRefOrCreate(ref as React.RefObject<HTMLInputElement | null>) | ||
| const [characterCount, setCharacterCount] = useState<string>('') | ||
| const [isOverLimit, setIsOverLimit] = useState<boolean>(false) |
There was a problem hiding this comment.
nit: Can this be inferred instead of stored in state? Might save us a re-render?
const isOverLimit = characterCount > characterLimit
There was a problem hiding this comment.
I tried storing it both as a ref and a constant and updating it in a useEffect, but since it needs to update on every keystroke it wasn't quite working for me. Do you have another suggestion or do you think it's ok as-is? I see you added it's a "nit" so wanted to check!
There was a problem hiding this comment.
yep, called it a nit because I assumed you must have had a reason.
but since it needs to update on every keystroke it wasn't quite working for me.
that's odd, I assumed setCharacterCount would cause a re-render which can be used to calculate isOverLimit.
anywho, not ideal but also not blocking
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/9302 |
siddharthkp
commented
Dec 20, 2025
I see that one of the checks is still pending here but completed in the integration branch 🤔 Going to add |
Uh oh!
There was an error while loading. Please reload this page.
Related to https://github.com/github/primer/issues/5937.
Related PVC PR
Below is a video showcasing that TextArea and TextField components have an optional
characterLimitthat can be passed in as a prop. When a user types, the field is updated with how many characters are left / how many are over. When a user exceeds the limit, the character count text changes to red and an error icon prepends the text.There is also an
aria-liveregion that updates after a slight delay (500ms) when a user finishes typing. This accurately tells screen reader users how many characters they have left / are over, as well as when the input is invalid if they have typed too many characters.We are also including
sr-onlytext that is associated with the input so that when a user focuses on the input, they hear "You can enter up to X character(s)". This was added because associating the "X character(s) remaining" message was causing duplicate announcements in NVDA and this is the recommended approach.Components._.Textarea._.Features.-.With.Character.Limit.Storybook.-.19.December.2025.mp4
Changelog
New
characterLimitprop toTextAreacharacterLimitprop toTextInputcharacter_counter.tsfunctionalityChanged
Removed
Rollout strategy
Testing & Reviewing
Merge checklist