npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xsolla/xui-textarea

v0.216.1

Published

A cross-platform React textarea component for multi-line text input. Includes error and warning states and validation message support. <!-- BEGIN:xui-mcp-instructions:textarea --> A multi-line text input for collecting longer freeform content. Supports fi

Downloads

16,264

Readme

Textarea

A cross-platform React textarea component for multi-line text input. Includes error and warning states and validation message support.

A multi-line text input for collecting longer freeform content. Supports five sizes, an optional scroll bar, an optional resize handle, placeholder text, and the full standard input state set. Used wherever the expected input length exceeds a single line — comments, descriptions, messages, notes, and code snippets.

When to use

When the user needs to enter multiple sentences, paragraphs, or structured freeform content — descriptions, comments, feedback, notes, bios, addresses

When the expected input length is unpredictable or variable and a fixed single-line field would feel too restrictive

In forms where a content field needs to stand visually apart from short text inputs by its height

For developer or admin contexts where code, JSON, or multi-line configuration is entered

When not to use

  • For short, single-value inputs (name, email, URL) — use a standard Input
  • When the user must select from predefined options — use Select or ContextMenu
  • When rich formatting (bold, links, lists) is needed — use a rich-text editor, not TextArea
  • For search queries or single-line filter inputs — use Input with appropriate type

Content guidelines

  • Labels should be short, specific, and written in sentence case: "Description", "Additional notes", "Message", not "DESCRIPTION".
  • Use labels that clearly describe the expected content: "Project summary", "Delivery instructions", "Tell us more", not vague labels like "Text" or "Write here".
  • Helper text should clarify purpose, expected level of detail, or constraints when needed: "Include key goals and timelines", "Maximum 500 characters".
  • Placeholder text should support the label, not replace it. Use short examples or prompts only when they reduce ambiguity, and avoid placing essential instructions in the placeholder because it disappears as the user types.
  • Error messages should be explicit and actionable: "Enter a description", "Message must be 500 characters or fewer" — not generic messages like "Invalid input".
  • Keep labels concise. If the field needs more explanation, add helper text nearby — not in the label or placeholder.

Behaviour guidelines (from industry practice)

Auto-grow — for most form contexts, prefer auto-grow behaviour: the field starts at the Size height and expands vertically as the user types, rather than scrolling. Pair this with Resize handle=false and Scroll bar=false. Implement via JavaScript (scrollHeight technique) or a CSS field-sizing: content property.

Fixed height with scroll — for contexts where layout stability matters (e.g. a message composer in a chat UI with a fixed panel), use a fixed height and enable Scroll bar=true so the user can scroll the content within the field.

Character count — if the field has a maximum length, display a character counter below the field (e.g. 240 / 500). Update the counter in real time as the user types. When the limit is reached, prevent further input and switch to State=Error with a descriptive message.

Validation — validate on blur (when the user leaves the field) or on form submit. Do not show errors while the user is actively typing. Switch to State=Error with a specific error message when the value fails validation.

Paste — always allow paste. Do not block Ctrl+V / Cmd+V. If content from paste exceeds the character limit, truncate to the limit and show the character counter at maximum.

Tab key — in most form contexts, Tab should move focus to the next field. In code editors or developer-facing TextAreas where tab indentation is expected, handle Tab to insert a tab character and provide a clear way to exit the field (e.g. Escape followed by Tab, or a documented keyboard shortcut).

Disabled state — the field is not focusable and not editable. If a value is present, it remains visible in muted style. Provide an explanation (tooltip or nearby label) if the reason for disabling is not obvious.

Placeholder disappears — placeholder text is shown only in the empty state. It is not a label and must not be relied upon as the sole description of the field.

Accessibility

Use a native element — it inherits keyboard focus, selection, and browser spellcheck behaviour for free.

Always provide a visible label associated via or aria-labelledby. Do not use placeholder as the only label — it disappears when the user starts typing and is not reliably announced by all screen readers.

When State=Error, the error message must be linked via aria-describedby so screen readers announce it when the field receives focus.

When State=Disable, the element must have the disabled attribute. Disabled fields are not focusable by keyboard.

If a character counter is shown, it must be in an aria-live="polite" region so screen readers announce the count as it updates. Link it to the field via aria-describedby.

If Tab is intercepted to insert indentation (developer use case), provide an accessible escape mechanism and document it: include an aria-description on the field noting how to exit — e.g. "Press Escape then Tab to move to the next field".

The resize handle must be keyboard-operable if implemented. Consider providing ↑ / ↓ arrow key controls to adjust the height when the handle has focus, with aria-label="Resize text field".

Installation

npm install @xsolla/xui-textarea

Validation states

| State | Prop | Meaning | | :-------- | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | | Error | errorMessage (or state="error") | Blocking. The value is invalid and must be fixed before the form can be submitted. | | Warning | warningMessage (or state="warning") | Non-blocking. The value is allowed but needs attention — e.g. an empty optional field the user probably meant to fill in. |

Passing errorMessage or warningMessage implies the matching state, so state is only needed when you want the border highlight without a message below the field — for forms that render validation text somewhere else (a form-level error summary, a shared label, a tooltip).

Error takes precedence over warning: when both are supplied, the field renders as an error and the warning is suppressed.

Demo

Basic Textarea

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function BasicTextarea() {
  const [value, setValue] = React.useState("");

  return (
    <TextArea
      value={value}
      onChangeText={setValue}
      placeholder="Enter your message..."
    />
  );
}

Textarea Sizes

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaSizes() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <TextArea size="xs" placeholder="Extra Small" />
      <TextArea size="sm" placeholder="Small" />
      <TextArea size="md" placeholder="Medium (default)" />
      <TextArea size="lg" placeholder="Large" />
      <TextArea size="xl" placeholder="Extra Large" />
    </div>
  );
}

Textarea with Error

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaWithError() {
  const [value, setValue] = React.useState("");
  const maxLength = 100;

  return (
    <TextArea
      value={value}
      onChangeText={setValue}
      placeholder="Enter description..."
      errorMessage={
        value.length > maxLength
          ? `Maximum ${maxLength} characters allowed`
          : ""
      }
    />
  );
}

Textarea with Warning

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaWithWarning() {
  const [value, setValue] = React.useState("");

  return (
    <TextArea
      value={value}
      onChangeText={setValue}
      placeholder="Description (optional)"
      warningMessage={
        value.trim() === ""
          ? "Adding a description helps reviewers understand your request"
          : undefined
      }
    />
  );
}

Border Highlight Without a Message

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaBorderHighlight() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {/* The validation text lives in a form-level summary elsewhere */}
      <TextArea state="warning" placeholder="Needs attention" />
      <TextArea state="error" placeholder="Invalid" />
    </div>
  );
}

Textarea with Blur Validation

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaBlurValidation() {
  const [value, setValue] = React.useState("");
  const [error, setError] = React.useState<string | undefined>(undefined);

  return (
    <TextArea
      value={value}
      onChangeText={setValue}
      onBlur={() =>
        setError(value.trim() === "" ? "This field is required" : undefined)
      }
      onFocus={() => setError(undefined)}
      errorMessage={error}
      placeholder="Required field"
    />
  );
}

Anatomy

Import the component and use it directly:

import { TextArea } from "@xsolla/xui-textarea";

<TextArea
  value={text} // Controlled value
  onChangeText={setText} // Value change handler
  placeholder="Placeholder" // Placeholder text
  size="md" // Size variant
  disabled={false} // Disabled state
  state="default" // Border highlight without a message
  errorMessage="Error text" // Error message below textarea
  warningMessage="Warning text" // Warning message below textarea
/>;

Examples

Comment Form

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";
import { Button } from "@xsolla/xui-button";

export default function CommentForm() {
  const [comment, setComment] = React.useState("");

  const handleSubmit = () => {
    console.log("Comment:", comment);
    setComment("");
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
      <TextArea
        value={comment}
        onChangeText={setComment}
        placeholder="Write a comment..."
        size="md"
      />
      <Button onPress={handleSubmit} disabled={!comment.trim()}>
        Post Comment
      </Button>
    </div>
  );
}

Disabled Textarea

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function DisabledTextarea() {
  return (
    <TextArea
      value="This content cannot be edited"
      disabled
      placeholder="Disabled textarea"
    />
  );
}

Soft Validation on Blur

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaSoftValidation() {
  const [value, setValue] = React.useState("");
  const [warning, setWarning] = React.useState<string | undefined>(undefined);

  // An empty value is permitted, so the form can still be submitted — the
  // field simply nudges the user instead of reporting an error.
  return (
    <TextArea
      value={value}
      onChangeText={setValue}
      onBlur={() =>
        setWarning(
          value.trim() === ""
            ? "Adding a description helps reviewers understand your request"
            : undefined
        )
      }
      onFocus={() => setWarning(undefined)}
      warningMessage={warning}
      placeholder="Description (optional)"
    />
  );
}

Character Counter

import * as React from "react";
import { TextArea } from "@xsolla/xui-textarea";

export default function TextareaWithCounter() {
  const [value, setValue] = React.useState("");
  const maxLength = 500;

  return (
    <div>
      <TextArea
        value={value}
        onChangeText={setValue}
        placeholder="Enter your bio..."
        errorMessage={
          value.length > maxLength ? "Character limit exceeded" : ""
        }
      />
      <div
        style={{
          textAlign: "right",
          fontSize: 12,
          color: value.length > maxLength ? "red" : "gray",
        }}
      >
        {value.length}/{maxLength}
      </div>
    </div>
  );
}

API Reference

TextArea

A multi-line text input component.

TextArea Props:

| Prop | Type | Default | Description | | :--------------- | :--------------------------------------- | :------------ | :-------------------------------------------------------------------------------------------------------------- | | testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. | | value | string | - | The controlled value of the textarea. | | placeholder | string | - | Placeholder text when empty. | | onChangeText | (text: string) => void | - | Callback when text changes. | | onBlur | () => void | - | Callback fired when the textarea loses focus. Ideal for blur-based validation. | | onFocus | () => void | - | Callback fired when the textarea gains focus. | | size | "xl" \| "lg" \| "md" \| "sm" \| "xs" | "md" | Size of the textarea. | | disabled | boolean | false | Whether the textarea is disabled. | | state | "default" \| "warning" \| "error" | "default" | Validation state driving the border highlight, independent of any message. Error takes precedence over warning. | | errorMessage | string | - | Error message displayed below. Implies state="error". | | warningMessage | string | - | Warning message displayed below. Implies state="warning". Ignored in the error state. | | aria-label | string | - | Accessible label. | | aria-describedby | string | - | ID of description element. | | id | string | - | HTML id attribute. | | testID | string | - | Test identifier. |

Padding by Size:

| Size | Padding | | :--- | :------ | | xl | 16px | | lg | 14px | | md | 12px | | sm | 10px | | xs | 10px |

Validation state tokens:

| State | Border | Message text | | :------ | :---------------------------- | :--------------------------------------- | | error | theme.colors.border.alert | theme.colors.content.alert.primary | | warning | theme.colors.border.warning | theme.colors.content.warning.primary |

Accessibility

  • Uses native <textarea> element
  • aria-invalid set for the error state only — a warning is not an invalid value, so it is never marked invalid
  • aria-disabled for disabled state
  • aria-describedby links to the error or warning message when one is rendered
  • Error message has role="alert" for immediate announcements
  • Warning message has role="status" so it is announced politely, without interrupting the user
  • Focus indicator follows WCAG guidelines
  • Because a border highlight alone is a colour-only signal, when using state without a message make sure the corresponding validation text is rendered elsewhere and associated with the field via aria-describedby