Buttons
Data Display
Layout
Navigation
Password Input
Password field with a button that shows or hides the password.
React
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputDemo() {
return (
<PasswordInput.Root maxW="xs">
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Built on Ark UI's Password Input. The input shares its sizes and variants with the input recipe, and the visibility
trigger sits inside the input's border at the end. Only React has this component so far.
Usage#
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputDemo() {
return (
<PasswordInput.Root maxW="xs">
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}<PasswordInput.Root>
<PasswordInput.Label />
<PasswordInput.Control>
<PasswordInput.Input />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator />
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>PasswordInput.Indicator renders its children while the password shows and its fallback while it is hidden. The icons
here are view and view-off from the active sprite, so they follow
your own icons.
Examples#
Sizes#
Set size on the root to xs, sm, md or lg. The default is md.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
import { Stack } from "styled-system/jsx"
export function PasswordInputSizesDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<PasswordInput.Root size="xs">
<PasswordInput.Label>size xs</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<PasswordInput.Root size="sm">
<PasswordInput.Label>size sm</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<PasswordInput.Root size="md">
<PasswordInput.Label>size md</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<PasswordInput.Root size="lg">
<PasswordInput.Label>size lg</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
</Stack>
)
}Variants#
Set variant on the root to outline, subtle or flushed. The default is outline.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
import { Stack } from "styled-system/jsx"
export function PasswordInputVariantsDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<PasswordInput.Root variant="outline">
<PasswordInput.Label>variant outline</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<PasswordInput.Root variant="subtle">
<PasswordInput.Label>variant subtle</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<PasswordInput.Root variant="flushed">
<PasswordInput.Label>variant flushed</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Enter your password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
</Stack>
)
}Default visible#
Set defaultVisible to show the password when the input first renders.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputDefaultVisibleDemo() {
return (
<PasswordInput.Root defaultVisible maxW="xs">
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input defaultValue="boring-but-safe" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Controlled#
Pass visible and onVisibilityChange to own whether the password shows.
Password is hidden
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
import { useState } from "react"
import { Stack, styled } from "styled-system/jsx"
export function PasswordInputControlledDemo() {
const [visible, setVisible] = useState(false)
return (
<Stack gap="3" w="full" maxW="xs">
<PasswordInput.Root visible={visible} onVisibilityChange={(details) => setVisible(details.visible)}>
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input defaultValue="boring-but-safe" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
<styled.p textStyle="sm" color="fg.muted">
Password is {visible ? "visible" : "hidden"}
</styled.p>
</Stack>
)
}Root provider#
Create the state with usePasswordInput and pass it to PasswordInput.RootProvider to control the input from outside,
here with toggleVisible. The returned API also has setVisible and focus.
import { Button } from "@isbatak/react-ui/button"
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput, usePasswordInput } from "@isbatak/react-ui/password-input"
import { Stack } from "styled-system/jsx"
export function PasswordInputRootProviderDemo() {
const passwordInput = usePasswordInput()
return (
<Stack gap="3" w="full" maxW="xs" alignItems="flex-start">
<Button variant="outline" size="sm" onClick={() => passwordInput.toggleVisible()}>
{passwordInput.visible ? "Hide" : "Show"} password
</Button>
<PasswordInput.RootProvider value={passwordInput}>
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input defaultValue="boring-but-safe" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.RootProvider>
</Stack>
)
}Autocomplete#
The input sets autocomplete="current-password" by default. Set autoComplete="new-password" on sign-up and
change-password forms so browsers offer to generate a password instead of filling a saved one.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputAutocompleteDemo() {
return (
<PasswordInput.Root autoComplete="new-password" maxW="xs">
<PasswordInput.Label>New password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Choose a password" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Ignore password managers#
Set ignorePasswordManagers for secrets that are not login passwords, like API keys, so password managers do not offer
to fill or save them. It works with 1Password, LastPass, Bitwarden, Dashlane and Proton Pass.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputIgnorePasswordManagersDemo() {
return (
<PasswordInput.Root ignorePasswordManagers maxW="xs">
<PasswordInput.Label>API key</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Paste your API key" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Disabled#
Set disabled on the root to disable the input and the visibility trigger.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputDisabledDemo() {
return (
<PasswordInput.Root disabled maxW="xs">
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input defaultValue="boring-but-safe" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Invalid#
Set invalid to give the input the error border. Add the error text below it, here with the field recipe's
errorText. readOnly and required go on the root the same way.
import { field } from "@isbatak/panda-ds/recipes"
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputInvalidDemo() {
const fieldClasses = field()
return (
<PasswordInput.Root invalid maxW="xs">
<PasswordInput.Label>Password</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input defaultValue="short" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
<span className={fieldClasses.errorText}>Use at least 8 characters.</span>
</PasswordInput.Root>
)
}Translations#
The visibility trigger's label is "Show password" or "Hide password". Pass translations.visibilityTrigger to change
it; it gets whether the password is visible.
import { Icon } from "@isbatak/react-ui/icon"
import { PasswordInput } from "@isbatak/react-ui/password-input"
export function PasswordInputTranslationsDemo() {
return (
<PasswordInput.Root
translations={{ visibilityTrigger: (visible) => (visible ? "Passwort verbergen" : "Passwort anzeigen") }}
maxW="xs"
>
<PasswordInput.Label>Passwort</PasswordInput.Label>
<PasswordInput.Control>
<PasswordInput.Input placeholder="Passwort eingeben" />
<PasswordInput.VisibilityTrigger>
<PasswordInput.Indicator fallback={<Icon name="view-off" />}>
<Icon name="view" />
</PasswordInput.Indicator>
</PasswordInput.VisibilityTrigger>
</PasswordInput.Control>
</PasswordInput.Root>
)
}Recipe#
| Variant | Values | Default |
|---|---|---|
variant | outline, subtle, flushed | outline |
size | xs, sm, md, lg | md |
| Variable | Description |
|---|---|
--input-height | The input height, set by size. The visibility trigger is a square of it |
--focus-color | The focus ring color, colors.colorPalette.focusRing |
--error-color | The border and focus ring color when invalid, colors.border.error |