Textarea

Textarea allows users to enter multi-line text. Supports labels, validation, character count, and reactive state binding.

Basic Examples

Simple textareas with various configurations

await import('/components/data-input/textarea.js');
const { tags, $ } = Lightview;
const { div, Textarea } = tags;

// 1. Basic textarea
const basic = Textarea({ 
    placeholder: 'Enter your message...',
    rows: 3
});

// 2. Textarea with label and helper
const withLabel = Textarea({ 
    label: 'Bio',
    placeholder: 'Tell us about yourself...',
    helper: 'Max 500 characters',
    rows: 4
});

// 3. Textarea with character count
const withCount = Textarea({ 
    label: 'Description',
    placeholder: 'Product description...',
    maxLength: 200,
    showCount: true,
    rows: 3
});

// Insert all examples
$('#example').content(
    div({ style: 'display: flex; flex-direction: column; gap: 1rem' }, basic, withLabel, withCount)
);
await import('/components/data-input/textarea.js');
const { $, tags } = Lightview;
const { Textarea, div } = tags;

const basic = {
    tag: Textarea,
    attributes: { 
        placeholder: 'Enter your message...',
        rows: 3
    }
};

const withLabel = {
    tag: Textarea,
    attributes: { 
        label: 'Bio',
        placeholder: 'Tell us about yourself...',
        helper: 'Max 500 characters',
        rows: 4
    }
};

const withCount = {
    tag: Textarea,
    attributes: { 
        label: 'Description',
        placeholder: 'Product description...',
        maxLength: 200,
        showCount: true,
        rows: 3
    }
};

$('#example').content({
    tag: div,
    attributes: { style: 'display: flex; flex-direction: column; gap: 1rem' },
    children: [basic, withLabel, withCount]
});
await import('/components/data-input/textarea.js');
const { $ } = Lightview;

$('#example').content({
    div: {
        style: 'display: flex; flex-direction: column; gap: 1rem',
        children: [
            { 
                Textarea: { 
                    placeholder: 'Enter your message...',
                    rows: 3
                } 
            },
            { 
                Textarea: { 
                    label: 'Bio',
                    placeholder: 'Tell us about yourself...',
                    helper: 'Max 500 characters',
                    rows: 4
                } 
            },
            { 
                Textarea: { 
                    label: 'Description',
                    placeholder: 'Product description...',
                    maxLength: 200,
                    showCount: true,
                    rows: 3
                } 
            }
        ]
    }
});

Reactive Example

Two-way binding with signals and live validation

await import('/components/data-input/textarea.js');
const { signal, tags, $ } = Lightview;
const { div, p, span, Textarea } = tags;

// Signal for two-way binding
const feedback = signal('');

// Validation function
const validateFeedback = (val) => {
    if (!val || val.trim().length === 0) return 'Feedback is required';
    if (val.length < 20) return 'Please provide more detail (at least 20 characters)';
    if (val.length > 500) return 'Feedback is too long (max 500 characters)';
    return null;
};

// Reactive textarea with validation
const reactiveDemo = div({ style: 'display: flex; flex-direction: column; gap: 1rem' },
    Textarea({ 
        label: 'Your Feedback',
        placeholder: 'Share your thoughts with us...',
        value: feedback,
        validate: validateFeedback,
        showCount: true,
        maxLength: 500,
        required: true,
        rows: 4
    }),
    p({ class: 'text-sm' }, 
        () => {
            const error = validateFeedback(feedback.value);
            return error 
                ? span({ class: 'text-error' }, '✗ ' + error)
                : span({ class: 'text-success' }, '✓ Feedback is valid!');
        }
    )
);

$('#example').content(reactiveDemo);
await import('/components/data-input/textarea.js');
const { signal, $, tags } = Lightview;
const { Textarea, div, p, span } = tags;

const feedback = signal('');

const validateFeedback = (val) => {
    if (!val || val.trim().length === 0) return 'Feedback is required';
    if (val.length < 20) return 'Please provide more detail (at least 20 characters)';
    if (val.length > 500) return 'Feedback is too long (max 500 characters)';
    return null;
};

const reactiveDemo = {
    tag: div,
    attributes: { style: 'display: flex; flex-direction: column; gap: 1rem' },
    children: [
        {
            tag: Textarea,
            attributes: {
                label: 'Your Feedback',
                placeholder: 'Share your thoughts with us...',
                value: feedback,
                validate: validateFeedback,
                showCount: true,
                maxLength: 500,
                required: true,
                rows: 4
            }
        },
        {
            tag: p,
            attributes: { class: 'text-sm' },
            children: [
                () => {
                    const error = validateFeedback(feedback.value);
                    return error 
                        ? { tag: span, attributes: { class: 'text-error' }, children: ['✗ ' + error] }
                        : { tag: span, attributes: { class: 'text-success' }, children: ['✓ Feedback is valid!'] };
                }
            ]
        }
    ]
};

$('#example').content(reactiveDemo);
await import('/components/data-input/textarea.js');
const { signal, $ } = Lightview;

const feedback = signal('');

const validateFeedback = (val) => {
    if (!val || val.trim().length === 0) return 'Feedback is required';
    if (val.length < 20) return 'Please provide more detail (at least 20 characters)';
    if (val.length > 500) return 'Feedback is too long (max 500 characters)';
    return null;
};

const reactiveDemo = {
    div: {
        style: 'display: flex; flex-direction: column; gap: 1rem',
        children: [
            {
                Textarea: {
                    label: 'Your Feedback',
                    placeholder: 'Share your thoughts with us...',
                    value: feedback,
                    validate: validateFeedback,
                    showCount: true,
                    maxLength: 500,
                    required: true,
                    rows: 4
                }
            },
            {
                p: {
                    class: 'text-sm',
                    children: [
                        () => {
                            const error = validateFeedback(feedback.value);
                            return error 
                                ? { span: { class: 'text-error', children: ['✗ ' + error] } }
                                : { span: { class: 'text-success', children: ['✓ Feedback is valid!'] } };
                        }
                    ]
                }
            }
        ]
    }
};

$('#example').content(reactiveDemo);

Props

Prop Type Default Description
value string | signal - Textarea value (reactive with signals)
defaultValue string '' Default value for uncontrolled mode
placeholder string - Placeholder text
label string - Label text displayed above textarea
helper string - Helper text displayed below the textarea
error string | function - Error message or reactive error function
validate function - Validation function: (value) => errorMessage | null
rows number 3 Number of visible text lines
maxLength number - Maximum character length
showCount boolean false Show character count
color string - 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
size string 'md' 'xs' | 'sm' | 'md' | 'lg'
ghost boolean false Ghost style (no background)
disabled boolean false Disable the textarea
readOnly boolean false Make textarea read-only
required boolean false Mark as required field (shows asterisk)
onChange function - Callback when value changes: (value, event) => void
useShadow boolean * Render in Shadow DOM. *Follows global initComponents() setting

Sizes

Colors

With Label (Fieldset Pattern)

Bio

Max 500 characters.