> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Text input

> Single-line, password, and multi-line text editing controls

export const PlatformStatuses = ({statuses}) => {
  const StatusBadge = ({status, label}) => {
    const styles = {
      green: {
        backgroundColor: '#dcfce7',
        color: '#166534'
      },
      orange: {
        backgroundColor: '#fed7aa',
        color: '#9a3412'
      },
      red: {
        backgroundColor: '#fecaca',
        color: '#991b1b'
      },
      gray: {
        backgroundColor: '#f3f4f6',
        color: '#4b5563'
      }
    };
    const baseStyle = {
      display: 'inline-flex',
      alignItems: 'center',
      padding: '0.125rem 0.625rem',
      borderRadius: '9999px',
      fontSize: '0.875rem',
      fontWeight: '500'
    };
    const colorStyle = styles[status] || styles.green;
    return <span style={{
      ...baseStyle,
      ...colorStyle
    }}>
        {label || status}
      </span>;
  };
  const STATUS_CONFIG = {
    supported: {
      label: "Supported",
      color: "green"
    },
    partial: {
      label: "Partial",
      color: "orange"
    },
    "not-implemented": {
      label: "Not Implemented",
      color: "gray"
    }
  };
  const renderCard = (platform, value) => {
    if (!value) return null;
    const {status, note} = typeof value === "string" ? {
      status: value
    } : value;
    const config = STATUS_CONFIG[status];
    if (!config) return null;
    const titleMap = {
      ios: "SwiftUI",
      android: "Jetpack Compose",
      web: "Web"
    };
    return <Card key={platform} title={titleMap[platform] || platform}>
          <StatusBadge status={config.color} label={config.label} />
          {note && <div style={{
      marginTop: '0.5rem',
      fontSize: '0.875rem',
      color: '#6b7280'
    }}>
              {note}
            </div>}
      </Card>;
  };
  if (statuses == null) {
    return null;
  }
  return <Columns cols="3">
      {Object.entries(statuses).map(([platform, value]) => renderCard(platform, value))}
    </Columns>;
};

export const ComposeJS = ({code, name, height}) => {
  const encodedCode = useMemo(() => {
    if (!code) return "";
    try {
      return btoa(code);
    } catch (e) {
      console.error("Failed to encode code", e);
      return "";
    }
  }, [code]);
  if (!encodedCode) {
    return null;
  }
  return <iframe src={`https://www.metabind.ai/embed?code=${encodedCode}&name=${name ?? 'Example'}`} loading="lazy" style={{
    width: "100%",
    height: height || '350px',
    border: "1px solid #e5e7eb",
    borderRadius: "var(--rounded-2xl,1rem)",
    overflow: "hidden"
  }} title="ComposeJS Preview" />;
};

`TextField` is the standard single-line input. `SecureField` is the same shape but obscures its contents for passwords and other sensitive data. `TextEditor` is a multi-line editing area for longer-form text.

All three follow the same controlled-input pattern: pass the current `text` value and a `setText` callback. Style them with shared modifiers like `.textFieldStyle()`, `.keyboardType()`, and `.autocorrectionDisabled()`.

## TextField

A single-line text input field with an optional placeholder.

```typescript theme={null}
TextField(props: {
    placeholder?: string;
    text: string;
    setText: (text: string) => void;
}): Component;
```

<ParamField path="placeholder" type="string" optional>
  Placeholder text displayed when the field is empty.
</ParamField>

<ParamField path="text" type="string" required>
  The current text value of the field.
</ParamField>

<ParamField path="setText" type="(text: string) => void" required>
  Callback invoked when the user types. Use this to update your state.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: { status: "supported" },
}}
/>

**Basic text field**

```typescript theme={null}
const body = () => {
    const [name, setName] = useState("")
    return TextField({
        placeholder: "Enter your name",
        text: name,
        setText: setName
    })
}
```

**With style modifiers**

```typescript theme={null}
const body = () => {
    const [email, setEmail] = useState("")
    return TextField({
        placeholder: "Email address",
        text: email,
        setText: setEmail
    })
        .textFieldStyle("roundedBorder")
        .keyboardType("emailAddress")
        .autocorrectionDisabled(true)
        .padding(16)
}
```

**Form with multiple fields**

```typescript theme={null}
const body = () => {
    const [first, setFirst] = useState("")
    const [last, setLast] = useState("")
    return VStack({ spacing: 12 }, [
        TextField({ placeholder: "First name", text: first, setText: setFirst }),
        TextField({ placeholder: "Last name", text: last, setText: setLast }),
        Button("Submit", () => {})
    ])
        .padding(16)
}
```

## SecureField

A text input that obscures its contents, used for passwords and sensitive data.

```typescript theme={null}
SecureField(props: {
    placeholder?: string;
    text: string;
    setText: (text: string) => void;
}): Component;
```

<ParamField path="placeholder" type="string" optional>
  Placeholder text displayed when the field is empty.
</ParamField>

<ParamField path="text" type="string" required>
  The current text value of the field (displayed as masked characters).
</ParamField>

<ParamField path="setText" type="(text: string) => void" required>
  Callback invoked when the user types. Use this to update your state.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: { status: "supported" },
}}
/>

**Basic password field**

```typescript theme={null}
const body = () => {
    const [password, setPassword] = useState("")
    return SecureField({
        placeholder: "Password",
        text: password,
        setText: setPassword
    })
}
```

**Login form**

```typescript theme={null}
const body = () => {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    return VStack({ spacing: 16 }, [
        TextField({
            placeholder: "Email",
            text: email,
            setText: setEmail
        })
            .textFieldStyle("roundedBorder"),
        SecureField({
            placeholder: "Password",
            text: password,
            setText: setPassword
        })
            .textFieldStyle("roundedBorder"),
        Button("Sign In", () => {})
            .disabled(email === "" || password === "")
    ])
        .padding(24)
}
```

## TextEditor

A multi-line text editing area for longer form text input.

```typescript theme={null}
TextEditor(props: { text: string; setText: (text: string) => void }): Component;
```

<ParamField path="text" type="string" required>
  The current text content displayed in the editor.
</ParamField>

<ParamField path="setText" type="(text: string) => void" required>
  Callback invoked when the user edits the text. Use this to update your state.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "partial", note: "setText callback is not implemented; text changes remain local only" },
web: { status: "supported" },
}}
/>

**Basic text editor**

```typescript theme={null}
const body = () => {
    const [text, setText] = useState("")
    return TextEditor({ text, setText })
}
```

**With frame and border**

```typescript theme={null}
const body = () => {
    const [notes, setNotes] = useState("")
    return TextEditor({ text: notes, setText: setNotes })
        .frame({ minHeight: 200 })
        .padding(8)
        .border(Color("gray"), 1)
        .cornerRadius(8)
}
```

**In a form layout**

```typescript theme={null}
const body = () => {
    const [title, setTitle] = useState("")
    const [body, setBody] = useState("")
    return VStack({ spacing: 16 }, [
        TextField({ placeholder: "Title", text: title, setText: setTitle }),
        TextEditor({ text: body, setText: setBody })
            .frame({ minHeight: 300 }),
        Button("Submit", () => {})
    ])
        .padding(16)
}
```

## See also

* [Text](/bindjs/components/Text) — read-only text display
* [Button](/bindjs/components/Button) — submit form actions
* [Toggle](/bindjs/components/Toggle) — boolean switch alongside fields in a form
* [VStack](/bindjs/components/VStack) — vertical layout for stacking form fields
