# PinInput A built-in tag for one-time-code or PIN entry — a row of single-character boxes that advance as you type. It is not an Input wrapper, so it has no label or message of its own; its `error` is a boolean that adds error styling to every box. Pair it with a heading or a [textinput](/components/inputs/textinput/) label when you need a caption. Use it as `{% pininput %}` in Markdown, or call it from Python logic (loops, snippets) via `component('aardvark', 'pininput', …)`. ## Basic PIN `length` sets the number of boxes (four by default).
Source: Markdown ```aardvark {% pininput length=4 %} ``` Source: Python ```python component('aardvark', 'pininput', length=4) ``` ## Length and type `type` is `number` (default, digits only) or `alphanumeric` (letters and digits).
Source: Markdown ```aardvark {% pininput length=6 type='number' %} {% pininput length=5 type='alphanumeric' %} ``` Source: Python ```python component('aardvark', 'pininput', length=6, type='number') component('aardvark', 'pininput', length=5, type='alphanumeric') ``` ## Mask and OTP autofill `mask` obscures entered characters like a password; `oneTimeCode` opts into the browser's one-time-code autofill so an SMS code can be filled automatically.
Source: Markdown ```aardvark {% pininput length=4 mask=true oneTimeCode=true %} ``` Source: Python ```python component('aardvark', 'pininput', length=4, mask=True, oneTimeCode=True) ``` ## Placeholder, size, radius, and gap `placeholder` sets the per-box placeholder character; `size` and `radius` style each box; `gap` is the space between boxes.
Source: Markdown ```aardvark {% pininput length=4 placeholder='○' size='lg' radius='xl' gap='md' %} ``` Source: Python ```python component('aardvark', 'pininput', length=4, placeholder='○', size='lg', radius='xl', gap='md') ``` ## Error and disabled `error` is a boolean that adds error styling and `aria-invalid` to every box; `disabled` makes the whole row inert.
Source: Markdown ```aardvark {% pininput length=4 error=true %} {% pininput length=4 disabled=true %} ``` Source: Python ```python component('aardvark', 'pininput', length=4, error=True) component('aardvark', 'pininput', length=4, disabled=True) ``` ## With other components PinInput has no label of its own, so pair it with a heading and supporting text inside a [card](/components/data-display/card/) to make a complete verification prompt. We sent a 6-digit code to your inbox.
Source: Markdown ```aardvark {% card title='Verify your email' %} We sent a 6-digit code to your inbox. {% pininput length=6 type='number' oneTimeCode=true %} {% endCard %} ``` Source: Python ```python body = ( 'We sent a 6-digit code to your inbox.\n\n' + component('aardvark', 'pininput', length=6, type='number', oneTimeCode=True) ) component('aardvark', 'card', title='Verify your email', children=body) ``` ## Attributes Omit any attribute to take its Mantine default. | Attribute | Valid values | Description | | --- | --- | --- | | `length` | integer | Number of single-character boxes. Default `4`. | | `type` | `number`, `alphanumeric` | Accepted characters. `number` (default) is digits only; `alphanumeric` allows letters and digits. | | `mask` | `true` / `false` | Obscure entered characters like a password. Default `false`. | | `oneTimeCode` | `true` / `false` | Opt into the browser's one-time-code (OTP) autofill. Default `false`. | | `placeholder` | string | Per-box placeholder character. | | `size` | `xs`, `sm`, `md`, `lg`, `xl` | Box size. | | `radius` | `xs`, `sm`, `md`, `lg`, `xl` (or any CSS value) | Box corner radius. | | `gap` | `xs`, `sm`, `md`, `lg`, `xl` (or any CSS value) | Space between boxes. | | `disabled` | `true` / `false` | Disable every box. Default `false`. | | `error` | `true` / `false` | Add error styling and `aria-invalid` to every box. Boolean — there is no wrapper for a message. Default `false`. | ## CSS Selectors Target a `{% pininput %}` from your own CSS with the island data attribute or the Mantine Styles API part classes: ```css /* Every PinInput instance on the page */ [data-aardvark-island="PinInput"] { } /* Mantine Styles API parts */ .mantine-PinInput-root { } .mantine-PinInput-pinInput { } .mantine-PinInput-input { } ``` ## Injecting Attributes Pass `attr={…}` to forward raw HTML attributes — including inline event handlers — straight onto the rendered element. Here it is wired to `onchange`, so changing the field logs its new value to the console and alerts it. For a PIN field that's a demonstration only — never log or transmit a real PIN value in production:
Source: Markdown ```aardvark {% pininput length=4 attr={'onchange': ''' const value = this.value; console.log('attr demo value:', value); alert(value); '''} %} ``` Source: Python ```python component('aardvark', 'pininput', length=4, attr={'onchange': ''' const value = this.value; console.log('attr demo value:', value); alert(value); '''}) ```