Build a fully custom payment form layout using individual field components and hooks.
The pre-built PaymentForm, CardForm, and BankAccountForm components cover most use cases. This page is for when you need complete control over the form layout, field arrangement, or submission behavior.
Use BaseInput to render iframe-hosted sensitive fields and the value field components for locally-rendered inputs. All components integrate with BiasProvider automatically.
| Component | Description |
|---|
BaseInput | Iframe-hosted field. Pass a type prop to select the field. |
CountrySelect | Country selection dropdown. |
PostalCodeInput | Postal code input with country-aware masking. |
SubmitButton | Submit button with loading and success animations. |
BaseInput accepts a type prop with one of these values:
| Type | Description |
|---|
"cardNumber" | Card number with brand detection. |
"cardExpiry" | Card expiration date (MM/YY). |
"cardCvc" | Card CVC / security code. |
"bankAccountNumber" | Bank account number. |
"bankRoutingNumber" | Bank routing number (ABA). |
import {
BiasProvider,
BaseInput,
CountrySelect,
PostalCodeInput,
SubmitButton,
usePaymentError,
} from "@biaspay/react";
function CustomCardForm() {
const error = usePaymentError();
return (
<div>
<BaseInput type="cardNumber" />
<div style={{ display: "flex", gap: "1rem" }}>
<BaseInput type="cardExpiry" />
<BaseInput type="cardCvc" />
</div>
<CountrySelect />
<PostalCodeInput />
{error && <div role="alert">{error}</div>}
<SubmitButton />
</div>
);
}
function CheckoutPage({ clientSecret }: { clientSecret: string }) {
return (
<BiasProvider clientSecret={clientSecret} onComplete={() => { /* ... */ }}>
<CustomCardForm />
</BiasProvider>
);
}
Use hooks to read form state, control submission, or manage individual value fields.
useField
Manage a single value field. Returns the field state and setter functions.
import { useField } from "@biaspay/react";
function CustomPostalCode() {
const { value, setValue, state } = useField("postalCode");
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
{state.error && <span>{state.error}</span>}
</div>
);
}
Pass a custom validator to override the default validation:
const { value, setValue } = useField("name", {
validate: (value, { showRequired }) => {
if (!value && showRequired) return { valid: false, error: "Name is required" };
if (value.length < 2) return { valid: false, error: "Name too short" };
return { valid: true, error: null };
},
});
useAttemptPayment
Returns a function that validates all fields and submits if the form is valid.
import { useAttemptPayment } from "@biaspay/react";
function CustomSubmitButton() {
const attemptPayment = useAttemptPayment();
return <button onClick={attemptPayment}>Pay now</button>;
}
useIsSubmittable
Returns true when all required fields are valid and the form is ready to submit.
import { useIsSubmittable, useAttemptPayment } from "@biaspay/react";
function CustomSubmitButton() {
const isSubmittable = useIsSubmittable();
const attemptPayment = useAttemptPayment();
return (
<button disabled={!isSubmittable} onClick={attemptPayment}>
Pay now
</button>
);
}
useSubmitState
Returns the current submission state.
import { useSubmitState } from "@biaspay/react";
const { loading, success } = useSubmitState();
usePaymentError
Returns the current payment error message, or null.
import { usePaymentError } from "@biaspay/react";
function PaymentError() {
const error = usePaymentError();
if (!error) return null;
return <div role="alert">{error}</div>;
}
useSelectedPaymentMethod
Returns the currently selected payment method ("card" or "us_bank_account").
import { useSelectedPaymentMethod } from "@biaspay/react";
const method = useSelectedPaymentMethod();
useSetSelectedPaymentMethod
Returns a function to change the selected payment method.
import { useSetSelectedPaymentMethod } from "@biaspay/react";
const setMethod = useSetSelectedPaymentMethod();
setMethod("us_bank_account");
useCheckoutSession
Returns the current CheckoutSession object, or undefined if not yet loaded.
import { useCheckoutSession } from "@biaspay/react";
const session = useCheckoutSession();
useOnComplete
Registers a completion callback. Alternative to the onComplete prop on BiasProvider.
import { useOnComplete } from "@biaspay/react";
useOnComplete(() => {
console.log("Payment complete");
});
Combine useSelectedPaymentMethod, useSetSelectedPaymentMethod, and individual field components to build a custom multi-method form.
import {
BiasProvider,
BaseInput,
CountrySelect,
PostalCodeInput,
SubmitButton,
useSelectedPaymentMethod,
useSetSelectedPaymentMethod,
usePaymentError,
useField,
} from "@biaspay/react";
function CustomPaymentForm() {
const method = useSelectedPaymentMethod();
const setMethod = useSetSelectedPaymentMethod();
const error = usePaymentError();
const name = useField("name");
return (
<div>
<div>
<button onClick={() => setMethod("card")}>Card</button>
<button onClick={() => setMethod("us_bank_account")}>Bank account</button>
</div>
{method === "card" && (
<div>
<BaseInput type="cardNumber" />
<BaseInput type="cardExpiry" />
<BaseInput type="cardCvc" />
</div>
)}
{method === "us_bank_account" && (
<div>
<input
value={name.value}
onChange={(e) => name.setValue(e.target.value)}
placeholder="Account holder name"
/>
<BaseInput type="bankRoutingNumber" />
<BaseInput type="bankAccountNumber" />
</div>
)}
<CountrySelect />
<PostalCodeInput />
{error && <div role="alert">{error}</div>}
<SubmitButton />
</div>
);
}
For full hook and component signatures, see the React reference and components reference.
The same field components and hooks are available from @biaspay/solid. The API is identical to React with two differences:
- Hooks that return reactive values return Solid accessors (functions). Call them to read the current value.
useField returns accessors for state and value.
import {
BiasProvider,
BaseInput,
CountrySelect,
PostalCodeInput,
SubmitButton,
usePaymentError,
useIsSubmittable,
useAttemptPayment,
} from "@biaspay/solid";
function CustomCardForm() {
const error = usePaymentError();
const isSubmittable = useIsSubmittable();
const attemptPayment = useAttemptPayment();
return (
<div>
<BaseInput type="cardNumber" />
<div style={{ display: "flex", gap: "1rem" }}>
<BaseInput type="cardExpiry" />
<BaseInput type="cardCvc" />
</div>
<CountrySelect />
<PostalCodeInput />
{error() && <div role="alert">{error()}</div>}
<button disabled={!isSubmittable()} onClick={attemptPayment}>
Pay now
</button>
</div>
);
}
Use @biaspay/web field elements directly when you need a custom layout without React or Solid. Import the package once, render the fields inside <bias-provider>, and add <bias-submit-button> to submit the payment.
<bias-provider id="checkout" client-secret="cs_test_...">
<div>
<bias-form-label label="Card number"></bias-form-label>
<bias-card-number-input></bias-card-number-input>
</div>
<div style="display: flex; gap: 1rem;">
<div>
<bias-form-label label="Expiration"></bias-form-label>
<bias-card-expiry-input></bias-card-expiry-input>
</div>
<div>
<bias-form-label label="Security code"></bias-form-label>
<bias-card-cvc-input></bias-card-cvc-input>
</div>
</div>
<bias-country-select></bias-country-select>
<bias-postal-code-input></bias-postal-code-input>
<bias-submit-button></bias-submit-button>
</bias-provider>
<script type="module">
import "@biaspay/web";
document.getElementById("checkout").addEventListener("complete", () => {
window.location.href = "/order-confirmed";
});
</script>
For all available custom elements and attributes, see the elements reference.