View as Markdown

Input Group

Add addons, buttons, and helper content to inputs.

Loading...
<twig:InputGroup class="max-w-xs">
    <twig:InputGroup:Input placeholder="Search..." />
    <twig:InputGroup:Addon>
        <twig:ux:icon name="lucide:search" />
    </twig:InputGroup:Addon>
    <twig:InputGroup:Addon align="inline-end">12 results</twig:InputGroup:Addon>
</twig:InputGroup>

Installation

php bin/console ux:install input-group --kit shadcn

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.12.0 tales-from-a-dev/twig-tailwind-extra:^1.0.0

Copy the following file(s) into your app:

{# @block content The input group elements, typically includes input, `InputGroup:Addon`, and/or `InputGroup:Button`. #}
&lt;div
    data-slot=&quot;input-group&quot;
    role=&quot;group&quot;
    class=&quot;{{ (&#039;group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[&gt;[data-align=block-end]]:h-auto has-[&gt;[data-align=block-end]]:flex-col has-[&gt;[data-align=block-start]]:h-auto has-[&gt;[data-align=block-start]]:flex-col has-[&gt;textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[&gt;[data-align=block-end]]:[&amp;&gt;input]:pt-3 has-[&gt;[data-align=block-start]]:[&amp;&gt;input]:pb-3 ltr:has-[&gt;[data-align=inline-end]]:[&amp;&gt;input]:pr-1.5 rtl:has-[&gt;[data-align=inline-end]]:[&amp;&gt;input]:pe-1.5 ltr:has-[&gt;[data-align=inline-start]]:[&amp;&gt;input]:pl-1.5 rtl:has-[&gt;[data-align=inline-start]]:[&amp;&gt;input]:ps-1.5 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/div&gt;
{# @prop align &#039;inline-start&#039;|&#039;inline-end&#039;|&#039;block-start&#039;|&#039;block-end&#039; The addon position relative to the input. #}
{# @block content The addon content, typically an icon or text. #}
{%- props align = &#039;inline-start&#039; -%}
{%- set style = html_cva(
    base: &quot;flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&amp;&gt;kbd]:rounded-[calc(var(--radius)-5px)] [&amp;&gt;svg:not([class*=&#039;size-&#039;])]:size-4&quot;,
    variants: {
        align: {
            &#039;inline-start&#039;: &#039;order-first ltr:pl-2 rtl:ps-2 ltr:has-[&gt;button]:ml-[-0.3rem] rtl:has-[&gt;button]:ms-[-0.3rem] ltr:has-[&gt;kbd]:ml-[-0.15rem] rtl:has-[&gt;kbd]:ms-[-0.15rem]&#039;,
            &#039;inline-end&#039;: &#039;order-last ltr:pr-2 rtl:pe-2 ltr:has-[&gt;button]:mr-[-0.3rem] rtl:has-[&gt;button]:me-[-0.3rem] ltr:has-[&gt;kbd]:mr-[-0.15rem] rtl:has-[&gt;kbd]:me-[-0.15rem]&#039;,
            &#039;block-start&#039;: &#039;order-first w-full justify-start px-2.5 pt-2 group-has-[&gt;input]/input-group:pt-2 [.border-b]:pb-2&#039;,
            &#039;block-end&#039;: &#039;order-last w-full justify-start px-2.5 pb-2 group-has-[&gt;input]/input-group:pb-2 [.border-t]:pt-2&#039;,
        },
    },
) -%}
&lt;div
    data-slot=&quot;input-group-addon&quot;
    data-align=&quot;{{ align }}&quot;
    role=&quot;group&quot;
    class=&quot;{{ style.apply({align: align}, attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/div&gt;
{# @prop type &#039;button&#039;|&#039;submit&#039; The button type. #}
{# @prop variant &#039;default&#039;|&#039;secondary&#039;|&#039;destructive&#039;|&#039;outline&#039;|&#039;ghost&#039;|&#039;link&#039; The visual style variant. #}
{# @prop size &#039;default&#039;|&#039;sm&#039;|&#039;lg&#039;|&#039;icon&#039;|&#039;icon-sm&#039;|&#039;icon-lg&#039; The button size. #}
{# @block content The button label and/or icon. #}
{%- props type = &#039;button&#039;, variant = &#039;ghost&#039;, size = &#039;xs&#039; -%}
{%- set style = html_cva(
    base: &#039;flex items-center gap-2 text-sm shadow-none&#039;,
    variants: {
        size: {
            xs: &quot;h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&amp;&gt;svg:not([class*=&#039;size-&#039;])]:size-3.5&quot;,
            sm: &#039;&#039;,
            &#039;icon-xs&#039;: &#039;size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[&gt;svg]:p-0&#039;,
            &#039;icon-sm&#039;: &#039;size-8 p-0 has-[&gt;svg]:p-0&#039;,
        },
    },
) %}
&lt;twig:Button
    type=&quot;{{ type }}&quot;
    variant=&quot;{{ variant }}&quot;
    size=&quot;{{ size }}&quot;
    class=&quot;{{ style.apply({size: size}, attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ ...attributes }}
&gt;
    {{- block(outerBlocks.content) -}}
&lt;/twig:Button&gt;
&lt;twig:Input
    data-slot=&quot;input-group-control&quot;
    class=&quot;{{ (&#039;flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ ...attributes.without(&#039;class&#039;) }}
/&gt;
{# @block content The text content displayed in the input group. #}
&lt;span
    data-slot=&quot;input-group-text&quot;
    class=&quot;{{ (&#039;text-muted-foreground flex items-center gap-2 text-sm [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*=size-])]:size-4 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/span&gt;
{# @block content The initial textarea value. #}
&lt;twig:Textarea
    data-slot=&quot;input-group-control&quot;
    class=&quot;{{ (&#039;flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ ...attributes.without(&#039;class&#039;) }}
&gt;{{- block(outerBlocks.content) -}}&lt;/twig:Textarea&gt;
{# @prop variant &#039;default&#039;|&#039;secondary&#039;|&#039;destructive&#039;|&#039;outline&#039;|&#039;ghost&#039;|&#039;link&#039; The visual style variant. #}
{# @prop size &#039;default&#039;|&#039;xs&#039;|&#039;sm&#039;|&#039;lg&#039;|&#039;icon&#039;|&#039;icon-xs&#039;|&#039;icon-sm&#039;|&#039;icon-lg&#039; The button size. #}
{# @prop as &#039;button&#039; The HTML tag to render. #}
{# @block content The button label and/or icon. #}
{%- props variant = &#039;default&#039;, size = &#039;default&#039;, as = &#039;button&#039; -%}
{%- set style = html_cva(
    base: &quot;group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#039;size-&#039;])]:size-4&quot;,
    variants: {
        variant: {
            default: &#039;bg-primary text-primary-foreground [a]:hover:bg-primary/80&#039;,
            outline: &#039;border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50&#039;,
            secondary: &#039;bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground&#039;,
            ghost: &#039;hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50&#039;,
            destructive: &#039;bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40&#039;,
            link: &#039;text-primary underline-offset-4 hover:underline&#039;,
        },
        size: {
            default: &#039;h-8 gap-1.5 px-2.5 ltr:has-data-[icon=inline-end]:pr-2 rtl:has-data-[icon=inline-end]:pe-2 ltr:has-data-[icon=inline-start]:pl-2 rtl:has-data-[icon=inline-start]:ps-2&#039;,
            xs: &quot;h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg ltr:has-data-[icon=inline-end]:pr-1.5 rtl:has-data-[icon=inline-end]:pe-1.5 ltr:has-data-[icon=inline-start]:pl-1.5 rtl:has-data-[icon=inline-start]:ps-1.5 [&amp;_svg:not([class*=&#039;size-&#039;])]:size-3&quot;,
            sm: &quot;h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg ltr:has-data-[icon=inline-end]:pr-1.5 rtl:has-data-[icon=inline-end]:pe-1.5 ltr:has-data-[icon=inline-start]:pl-1.5 rtl:has-data-[icon=inline-start]:ps-1.5 [&amp;_svg:not([class*=&#039;size-&#039;])]:size-3.5&quot;,
            lg: &#039;h-9 gap-1.5 px-2.5 ltr:has-data-[icon=inline-end]:pr-2 rtl:has-data-[icon=inline-end]:pe-2 ltr:has-data-[icon=inline-start]:pl-2 rtl:has-data-[icon=inline-start]:ps-2&#039;,
            icon: &#039;size-8&#039;,
            &#039;icon-xs&#039;: &quot;size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&amp;_svg:not([class*=&#039;size-&#039;])]:size-3&quot;,
            &#039;icon-sm&#039;: &#039;size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg&#039;,
            &#039;icon-lg&#039;: &#039;size-9&#039;,
        },
    },
) -%}
&lt;{{ as }}
    data-slot=&quot;button&quot;
    data-variant=&quot;{{ variant }}&quot;
    data-size=&quot;{{ size }}&quot;
    class=&quot;{{ style.apply({variant: variant, size: size}, attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/{{ as }}&gt;
&lt;input
    class=&quot;{{ (&#039;h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes.defaults({&#039;data-slot&#039;: &#039;input&#039;}) }}
&gt;
{# @block content The initial textarea value. #}
&lt;textarea
    data-slot=&quot;textarea&quot;
    class=&quot;{{ (&#039;flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/textarea&gt;

Usage

<twig:InputGroup>
    <twig:InputGroup:Input placeholder="Search..." />
    <twig:InputGroup:Addon>
        <twig:ux:icon name="lucide:search" />
    </twig:InputGroup:Addon>
</twig:InputGroup>

Examples

Icon

Loading...
<div class="grid w-full max-w-sm gap-6">
    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Search..." />
        <twig:InputGroup:Addon>
            <twig:ux:icon name="lucide:search" />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input type="email" placeholder="Enter your email" />
        <twig:InputGroup:Addon>
            <twig:ux:icon name="lucide:mail" />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Card number" />
        <twig:InputGroup:Addon>
            <twig:ux:icon name="lucide:credit-card" />
        </twig:InputGroup:Addon>
        <twig:InputGroup:Addon align="inline-end">
            <twig:ux:icon name="lucide:check" />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Card number" />
        <twig:InputGroup:Addon align="inline-end">
            <twig:ux:icon name="lucide:star" />
            <twig:ux:icon name="lucide:info" />
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

Text

Loading...
<div class="grid w-full max-w-sm gap-6">
    <twig:InputGroup>
        <twig:InputGroup:Addon>
            <twig:InputGroup:Text>$</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
        <twig:InputGroup:Input placeholder="0.00" />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Text>USD</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Addon>
            <twig:InputGroup:Text>https://</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
        <twig:InputGroup:Input placeholder="example.com" class="pl-0.5!" />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Text>.com</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Enter your username" />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Text>@company.com</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Textarea placeholder="Enter your message" />
        <twig:InputGroup:Addon align="block-end">
            <twig:InputGroup:Text class="text-xs text-muted-foreground">120 characters left</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

Button

Loading...
<div class="grid w-full max-w-sm gap-6">
    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="https://x.com/symfony" readonly />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Button aria-label="Copy" title="Copy" size="icon-xs">
                <twig:ux:icon name="lucide:copy" />
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup class="[--radius:9999px]">
        <twig:InputGroup:Addon>
            <twig:InputGroup:Button variant="secondary" size="icon-xs">
                <twig:ux:icon name="lucide:info" />
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
        <twig:InputGroup:Addon class="pl-1.5 text-muted-foreground">
            https://
        </twig:InputGroup:Addon>
        <twig:InputGroup:Input />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Button size="icon-xs">
                <twig:ux:icon name="lucide:star" />
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Type to search..." />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Button variant="secondary">Search</twig:InputGroup:Button>
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

Kbd

Loading...
<twig:InputGroup class="max-w-sm">
    <twig:InputGroup:Input placeholder="Search..." />
    <twig:InputGroup:Addon>
        <twig:ux:icon name="lucide:search" class="text-muted-foreground" />
    </twig:InputGroup:Addon>
    <twig:InputGroup:Addon align="inline-end">
        <twig:Kbd>⌘K</twig:Kbd>
    </twig:InputGroup:Addon>
</twig:InputGroup>

Spinner

Loading...
<div class="grid w-full max-w-sm gap-4">
    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Searching..." />
        <twig:InputGroup:Addon align="inline-end">
            <twig:Spinner />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Processing..." />
        <twig:InputGroup:Addon>
            <twig:Spinner />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Saving changes..." />
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Text>Saving...</twig:InputGroup:Text>
            <twig:Spinner />
        </twig:InputGroup:Addon>
    </twig:InputGroup>

    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Refreshing data..." />
        <twig:InputGroup:Addon>
            <twig:ux:icon name="lucide:loader" class="animate-spin" />
        </twig:InputGroup:Addon>
        <twig:InputGroup:Addon align="inline-end">
            <twig:InputGroup:Text class="text-muted-foreground">Please wait...</twig:InputGroup:Text>
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

Textarea

Loading...
<div class="grid w-full max-w-md gap-4">
    <twig:InputGroup>
        <twig:InputGroup:Textarea
            id="textarea-code-32"
            placeholder="console.log('Hello, world!');"
            class="min-h-[200px]"
        />
        <twig:InputGroup:Addon align="block-end" class="border-t">
            <twig:InputGroup:Text>Line 1, Column 1</twig:InputGroup:Text>
            <twig:InputGroup:Button size="sm" class="ml-auto" variant="default">
                Run <twig:ux:icon name="lucide:corner-down-left" />
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
        <twig:InputGroup:Addon align="block-start" class="border-b">
            <twig:InputGroup:Text class="font-mono font-medium">
                <twig:ux:icon name="ri:javascript-fill" />
                script.js
            </twig:InputGroup:Text>
            <twig:InputGroup:Button class="ml-auto" size="icon-xs">
                <twig:ux:icon name="lucide:refresh-ccw" />
            </twig:InputGroup:Button>
            <twig:InputGroup:Button variant="ghost" size="icon-xs">
                <twig:ux:icon name="lucide:copy" />
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

RTL

To enable RTL support, set the dir="rtl" attribute on the root element.

Loading...
<div class="w-full flex flex-col items-center gap-16">
    <div dir="rtl" class="grid w-full max-w-sm gap-6">
        <twig:InputGroup class="max-w-xs">
            <twig:InputGroup:Input placeholder="بحث..." />
            <twig:InputGroup:Addon>
                <twig:ux:icon name="lucide:search" />
            </twig:InputGroup:Addon>
            <twig:InputGroup:Addon align="inline-end">١٢ نتيجة</twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:InputGroup>
            <twig:InputGroup:Input placeholder="جاري البحث..." />
            <twig:InputGroup:Addon align="inline-end">
                <twig:Spinner />
            </twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:InputGroup>
            <twig:InputGroup:Input placeholder="جاري حفظ التغييرات..." />
            <twig:InputGroup:Addon align="inline-end">
                <twig:InputGroup:Text>جاري الحفظ...</twig:InputGroup:Text>
                <twig:Spinner />
            </twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:Field:Group class="max-w-sm">
            <twig:Field>
                <twig:Field:Label for="rtl-ar-textarea">منطقة النص</twig:Field:Label>
                <twig:InputGroup>
                    <twig:InputGroup:Textarea id="rtl-ar-textarea" placeholder="اكتب تعليقًا..." />
                    <twig:InputGroup:Addon align="block-end">
                        <twig:InputGroup:Text>٠/٢٨٠</twig:InputGroup:Text>
                        <twig:InputGroup:Button variant="default" size="sm" class="ms-auto">نشر</twig:InputGroup:Button>
                    </twig:InputGroup:Addon>
                </twig:InputGroup>
                <twig:Field:Description>تذييل موضع أسفل منطقة النص.</twig:Field:Description>
            </twig:Field>
        </twig:Field:Group>
    </div>

    <div dir="rtl" class="grid w-full max-w-sm gap-6">
        <twig:InputGroup class="max-w-xs">
            <twig:InputGroup:Input placeholder="הקלד..." />
            <twig:InputGroup:Addon>
                <twig:ux:icon name="lucide:search" />
            </twig:InputGroup:Addon>
            <twig:InputGroup:Addon align="inline-end">12 תוצאות</twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:InputGroup>
            <twig:InputGroup:Input placeholder="טוען..." />
            <twig:InputGroup:Addon align="inline-end">
                <twig:Spinner />
            </twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:InputGroup>
            <twig:InputGroup:Input placeholder="שומר שינויים..." />
            <twig:InputGroup:Addon align="inline-end">
                <twig:InputGroup:Text>שומר...</twig:InputGroup:Text>
                <twig:Spinner />
            </twig:InputGroup:Addon>
        </twig:InputGroup>

        <twig:Field:Group class="max-w-sm">
            <twig:Field>
                <twig:Field:Label for="rtl-he-textarea">אזור טקסט</twig:Field:Label>
                <twig:InputGroup>
                    <twig:InputGroup:Textarea id="rtl-he-textarea" placeholder="כתוב תגובה..." />
                    <twig:InputGroup:Addon align="block-end">
                        <twig:InputGroup:Text>0/280</twig:InputGroup:Text>
                        <twig:InputGroup:Button variant="default" size="sm" class="ms-auto">שלח</twig:InputGroup:Button>
                    </twig:InputGroup:Addon>
                </twig:InputGroup>
                <twig:Field:Description>כותרת תחתונה ממוקמת מתחת לאזור הטקסט.</twig:Field:Description>
            </twig:Field>
        </twig:Field:Group>
    </div>
</div>

API Reference

<twig:InputGroup>

Block Description
content The input group elements, typically includes input, InputGroup:Addon, and/or InputGroup:Button.

<twig:InputGroup:Addon>

Prop Type Default
align 
'inline-start'|'inline-end'|'block-start'|'block-end' 'inline-start'
Block Description
content The addon content, typically an icon or text.

<twig:InputGroup:Button>

Prop Type Default
type 
'button'|'submit' 'button'
variant 
'default'|'secondary'|'destructive'|'outline'|'ghost'|'link' 'ghost'
size 
'default'|'sm'|'lg'|'icon'|'icon-sm'|'icon-lg' 'xs'
Block Description
content The button label and/or icon.

<twig:InputGroup:Text>

Block Description
content The text content displayed in the input group.

<twig:InputGroup:Textarea>

Block Description
content The initial textarea value.