Input

Styled input with built-in reactivity and validation.

zweiundeins <sb-input> text field form validation Since 2026-09-21 2.6 kB0Open in playground Edit on GitHub

Playground

A text field with a label, help text, native constraint validation and an optional submit arrow. It exposes a live value property, so data-bind works as it does on a native <input>.

Examples

Label and hint

<sb-input label="Call sign" placeholder="e.g. Maverick" hint="Shown to the rest of the crew."></sb-input>

Validation

Validation runs after the first blur and then on every keystroke. required, minlength, pattern and type use the browser's own rules. Set error to replace the default message.

<sb-input label="Email" type="email" required placeholder="you@starbase.dev"></sb-input>
<sb-input label="Launch code" pattern="[0-9]{4}" error="Four digits, commander." placeholder="0000"></sb-input>

Two-way binding

Declare the signal first, because data-bind only writes to a signal that already has a value.

<div data-signals:_dest="''">
  <sb-input label="Destination" data-bind:_dest__prop.value__event.input placeholder="Type a planet"></sb-input>
  <p data-text="$_dest ? 'Plotting course to ' + $_dest + '…' : 'Awaiting destination'"></p>
</div>

Submit with the arrow

sb-submit fires on Enter or on the arrow button, but only when the value is valid. Send it to your backend with @post.

<div data-signals:_log="''">
  <sb-input action required placeholder="Transmit a message..." data-on:sb-submit="$_log = 'Sent: ' + evt.detail.value"></sb-input>
  <p data-text="$_log"></p>
</div>

With commands

Give it a name, and it emits sb-change with { name, value } when a value is committed: ready to post as a command. With confirm, it sets :state(pending) until the server's re-rendered attribute matches, and revert() goes back to the server's value when a command is rejected. See Commands and components and the Showcase.

Accessibility

The native <input> sits inside a <label>. Without a label, the placeholder becomes the accessible name. Errors are announced with role="alert", and aria-invalid follows the validation state.

Installation

Add Datastar with Rocket and the Starbase autoloader once per page, then use the tag. The autoloader imports each component the first time its tag appears, including tags added later by a Datastar morph.

<!-- Once per page: Datastar with Rocket, and the Starbase autoloader.
     It loads every <sb-…> component the first time its tag appears. -->
<script type="importmap">
  { "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/autoloader.js"></script>
<!-- Optional, no flash of undefined elements: class="sb-cloak" on <html>, and -->
<style>.sb-cloak :not(:defined) { visibility: hidden }</style>

<sb-input placeholder="Your message..." action></sb-input>

<!-- In production, pin today's catalog instead of the latest: the browser then
     refuses any file that changed. Add "integrity" to the import map above: the
     hashes from https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/importmap.json and Datastar's, below. -->
<!--
<script type="importmap">
  { "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" },
    "integrity": { "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js": "sha384-vUxZojLrF1Ar3de5h7VINqhJXBgjyZS4U49pHvGa87kum6j5Xn6JRziuARNw5ELG", "…": "…from importmap.json" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/autoloader.js" integrity="sha384-IvSCUjEWPS+wwXunU+48x96KmyWDx/Y4LeCwCL8cWFqJ/zHmTsTOsSMrzTOcpW3D"></script>
-->

<!-- Or load just this component, pinned to this version: -->
<!-- <script type="module" src="https://starbase.zweiundeins.gmbh/c/input@a54b301e44d1/input.js" integrity="sha384-OP1zdVmhpxyq4hP0KZXRF27Wg2Lpz3CBzPvJugZyRYikUhSKBLsSr5oQOqbkkqRI"></script> -->

Size

Each file compressed on its own, the way it is served (gzip -9, brotli -11). Datastar and Rocket are shared by every component and not counted.

FileOriginalgzipbrotli
input.js 7.9 kB3.0 kB2.6 kB

API reference

Props

AttributeTypeDefaultDescription
valuestring""The value. A new value from the server replaces it; the live value is the value property.
labelstring""Visible label.
placeholderstring""Placeholder text.
type"text" | "email" | "search" | "url" | "tel" | "password""text"Input type.
namestring""Name reported in sb-change and sb-submit (e.g. the field of a command).
requiredbooleanfalseValue must not be empty.
minlengthnumber0Minimum length.
patternstring""Regular expression the value must match.
hintstring""Help text below the field.
errorstring""Message shown when invalid (defaults to the browser message).
actionbooleanfalseShow a submit arrow button.
confirmbooleanfalseServer-confirmed value: :state(pending) while the local value differs from the server's value attribute (see revert()).

Events

NameDescription
inputOn every keystroke (native, re-targeted to the host).
changeWhen the value is committed.
sb-changeWhen the value is committed (blur or Enter). detail: { name, value }: ready for a command.
sb-submitEnter or arrow button with a valid value. detail: { name, value }.