React Implementation

The @secure-input/react package provides both a high-level drop-in component and a low-level hook to give you maximum flexibility.


Option 1: The SecureInput Component

The easiest way to get started is using the pre-built <SecureInput /> wrapper. It handles the Web Worker initialization and state management for you automatically.

CouponForm.tsx
import { SecureInput } from "@secure-input/react";

export function CouponForm() {
  const handleSubmit = async (encryptedValue: Uint8Array | string) => {
    // 1. You receive the encrypted payload
    // 2. Send it securely to your backend
    
    const response = await fetch("/api/validate-coupon", {
      method: "POST",
      headers: { "Content-Type": "text/plain" },
      body: encryptedValue,
    });

    const result = await response.json();
    console.log("Coupon valid:", result.valid);
  };

  return (
    <SecureInput
      placeholder="Enter your secret coupon code"
      onEncryptedSubmit={handleSubmit}
      showStatus={true} 
      className="border-accent"
    />
  );
}

Component Props

PropTypeDescription
onEncryptedSubmit(encrypted: string) => voidCalled on Enter key or internal form submit. Passes the cipher text.
showStatusbooleanOptional. Defaults to true. Shows visual indicator of WASM loading state.
inputPropsReact.InputHTMLAttributesOptional. Standard HTML input attributes to spread onto the internal input field.

Option 2: The useSecureInput Hook

If you need to build a custom UI (like integrating into a complex form library like React Hook Form), use the useSecureInput hook. This gives you manual control over exactly when encryption happens.

CustomCheckoutForm.tsx
import { useSecureInput } from "@secure-input/react";
import { useState } from "react";

export function CustomCheckoutForm() {
  const [plainTextInput, setPlainTextInput] = useState("");
  
  // Initialize the worker hook
  const { encrypt, isReady, error } = useSecureInput({
    autoInit: true,
    debug: false,
  });

  const handleCheckout = async (e: React.FormEvent) => {
    e.preventDefault();
    
    if (!isReady) return;

    // Trigger encryption right before network request
    const encryptedPayload = await encrypt(plainTextInput);
    
    // Clear the plain text state immediately for safety
    setPlainTextInput(""); 

    await fetch("/api/checkout", {
      method: "POST",
      body: JSON.stringify({ coupon: encryptedPayload })
    });
  };

  return (
    <form onSubmit={handleCheckout}>
      <input
        type="text"
        value={plainTextInput}
        onChange={(e) => setPlainTextInput(e.target.value)}
        disabled={!isReady}
        placeholder="Discount code"
      />
      <button type="submit" disabled={!isReady}>
        Apply & Checkout
      </button>
      
      {error && <p className="text-red-500">Encryption failed to load.</p>}
    </form>
  );
}

Best Practice: When using the hook, manually clear your React state (e.g. setPlainTextInput("")) immediately after you have generated the encrypted payload. This minimizes the window of time the plain text exists in memory.