A two column signup page with a cover image. Connect the form's onSubmit callback to your account service, then adapt the fields, branding and legal links. Account creation stays in your app; the block provides the registration interface. Explore validation and submission feedback in the live demo, then use the source and setup steps to integrate your onboarding flow.
Getting started
Add this block
Copy this variant into your Preact project, install the dependencies you are missing and connect your own authentication service. The source stays local, so you can change its layout without introducing another application framework.
1. Copy the block
Open the showcase’s Code tab and copy the files below relative to
src/components/blocks. Keep the source folder structure so relative imports resolve. These are destination paths; the Code tab’s display labels such asapp/signup/page.tsxare not the installation paths. The examples below assume your importing file issrc/App.tsx.signup/signup-02/page.tsx signup/signup-02/signup-form.tsx auth/shared/auth-utils.ts auth/shared/google-icon.tsx shared/branding/kamod-brand-link.tsx shared/branding/kamod-brand-sizes.ts shared/branding/kamod-logo.tsx shared/branding/kamod-logo-url.ts shared/branding/kamod-logo-horizontal.svg shared/branding/kamod-logo-horizontal-dark.svg auth/shared/auth-cover-url.ts auth/shared/auth-cover.svgForms use shared validation and provider helpers. Branded and illustrated layouts also need their listed SVGs and URL helpers. Keep
?urlimports in a Vite app, or adapt them to your bundler. If TypeScript cannot resolve an SVG import, include Vite’s client types (for example,import "vite/client"in an existing declaration file). Keep the repository’s license with your copied source.2. Install missing dependencies
Install only what your app does not already have. Preact renders the block, Kamod UI provides its interactive components, and Icons supplies its icons. Themes and Signals support the shared Kamod setup.
pnpm add @kamod-ch/ui @kamod-ch/icons preact @kamod-ch/themes @preact/signals3. Set up styles and import
Follow the theme and Tailwind setup. Import your global stylesheet and make sure Tailwind scans the copied source and Kamod components. Keep your existing setup if the app already uses Kamod.
import { Signup02 } from "./components/blocks/signup/signup-02/page";The
@kamod-ch/blocks/signup/signup-02path identifies source in this repository; the blocks package is private. Use the local import above rather than trying to install that path as a published package.Check the first render: the form controls, borders and page background should follow your app’s theme. If the layout appears unstyled, check the global CSS import and Tailwind source detection before changing the block’s classes. If an import fails, first compare your folder paths with the copy list; renaming only one file can break its relative imports.
Integration
Usage
Start by rendering the supplied page to check its appearance, then connect its local form to your application. Signup02 takes no props; callbacks belong to SignupForm inside the copied page. The form owns its input and feedback state, while your app owns the authenticated session and destination after success.
Render the page or form
import { Signup02 } from "./components/blocks/signup/signup-02/page";
export const App = () => <Signup02 />;This renders a complete page with its own main landmark. To preserve this variant’s layout, edit the SignupForm call in your copied page.tsx. To embed the form in an existing page or dialog, import the form directly instead of nesting another full page. Its heading is an h1; adjust that level and the field IDs if your host page already has a heading or another instance of this form.
Connect your authentication service
The example below wraps the form in a component that receives your service functions. Pass the same callbacks to the form in page.tsx if you keep the original layout. The imported value types describe the submitted payload; they are not configuration props on Signup02. See Form props for each callback and link destination.
import { SignupForm } from "./components/blocks/signup/signup-02/signup-form";
import type { SignupValues } from "./components/blocks/auth/shared/auth-utils";
type Props = {
submit: (values: SignupValues) => Promise<void>;
};
// These functions come from your application's authentication service.
export const AccountForm = ({ submit }: Props) => (
<SignupForm
onSubmit={submit}
loginHref="/login"
termsHref="/terms"
privacyHref="/privacy"
/>
);- Report the actual result
- Return the request’s promise so loading lasts until it settles. If your service resolves with an error result rather than rejecting, handle that result explicitly; the form otherwise treats completion as success. Map useful messages to the local error/status state in
signup-form.tsx. A callback’s return value does not automatically populate field errors or navigate to another page. - Replace the demo before connecting users
- Remove
sleep(),demoRejects()and the simulated success/error messages. Without a callback, the demo can still report success. Supply real account links and implement your success destination; the block does not create a session or redirect on its own.
Check the complete flow
- Invalid and valid submissions. Check the first invalid field receives focus, correct its input, then verify a successful request and a rejected request. Local field errors are recomputed on submission, not cleared as each character is typed.
- Pending requests. Buttons disable while loading, but inputs remain editable. Decide whether your app should freeze those fields and prevent repeated submit events; a disabled button alone is not a complete request guard. Handle cancellation if the form can unmount while a request is pending.
- Alternative paths. Test every visible provider button and account link. Social signup bypasses the email form’s terms and field checks; apply any required consent step in that flow too.
The variant details explain this form’s payload and provider behavior. Finish with the accessibility checks using your real labels, errors and service responses.
API reference
Props and data
Signup02 is a ready-made demonstration page with no public props. The reference below describes the local form you can configure after copying the source. Definitions come directly from the implementation; descriptions explain where your application takes over. Start with the component or data shape you want to change, then follow its type link to the complete definition.
import type from the corresponding copied file when typing your own data or component inputs. This reference describes the shipped source; changes in your local files will not update this page.TypeScriptForm props
? means the field may be omitted, not that every optional field has a fallback value. When a field accepts an array, its required marker means you must supply the array; check the helper’s behavior before using an empty [].| Prop / type | Description |
|---|---|
onSubmit | Receives name, email and password after the local field checks and terms acceptance pass. Return a promise so the form awaits account creation; thrown errors or rejections reach its error state. The copied form still runs its artificial delay and error-address check first and shows demo success even without a callback. Replace those behaviors and messages, and add any consent record your service requires because SignupValues does not contain the checkbox state. |
onSocialSignup | Called with github or google from the provider buttons when showSocial is enabled. This path is separate from the email form’s field and terms validation; a returned promise is awaited and failures use the social error state. Connect your provider registration flow and any consent requirements in your app. Without this callback the demo can still show success, and the AuthProvider union alone does not add a GitLab button. |
loginHref | URL for the Log in link below the signup form; defaults to #. Set it to the existing-account sign-in route appropriate for your application. The link does not submit the signup form or transfer its entered values. Use your router’s link component in the copied source if navigation must stay within a client-side router. |
termsHref | Destination for the Terms of Service link beside the signup consent checkbox; defaults to #. Link to the terms that apply to your product and keep that content available to users before they submit. The email form requires the checkbox locally, but this URL does not record acceptance and the SignupValues payload does not include it. Add any required consent data to your own submission flow. |
privacyHref | Destination for the Privacy Policy link in the signup form’s consent text; defaults to #. Supply the actual policy page for your product rather than leaving the demo anchor in place. This is a navigation prop only: it does not fetch policy content, track which version was read or add consent information to the submitted values. |
showSocial | Shows the GitHub and Google signup buttons and their separator above the email fields. It defaults to false on SignupForm, while the supplied Signup05 page explicitly enables it. Enabling the controls does not configure providers; supply onSocialSignup and connect the corresponding flow. The email form remains available, and hiding the social controls does not alter its validation. |
name | Name entered by the user and passed to the signup callback. Local validation requires at least two characters after trimming for the check, but the submitted value itself is not trimmed. Decide how your service normalizes and stores display names. This payload contains only name, email and password; the separately managed terms checkbox is not included. |
email | Email string passed to your submit callback after the form’s simple local format check. The copied form forwards the entered value without normalizing it; decide how your service handles surrounding whitespace and address casing. Passing the local check neither verifies ownership nor proves that an account exists. The demo also treats addresses containing error as a simulated failure before invoking onSubmit. |
password | Password string entered in the form and forwarded to onSubmit after the local minimum-length check of eight characters. The field does not trim or hash the value, and the demo’s validation is not an authentication service. Apply your application’s actual credential rules and submit through the appropriate service; avoid placing this value in logs or URLs. It is never included in the email-only MagicLinkValues payload. |
Data type reference
Expand a definition to copy its exact TypeScript shape. Required fields are listed below each summary for fields declared in that definition. The code header shows the file's installation path; copy the definition with the button beside it. For intersections, also inspect the referenced base type’s required fields. Local types can be imported from the same files as your copied components; they are not added to the zero-prop page wrapper.
SignupFormPropsSignupValuesname, email, passwordAuthProviderVariant details
Registration and consent
Registration validates a name, email and password, and asks the user to accept the terms. This combines field feedback with an explicit consent step; it does not create an account or store legal acceptance by itself.
- Consent is separate from the payload
- The checkbox is validated locally, but
SignupValuescontains only name, email and password. Extend your copied form and service contract if you need to persist consent, policy version or acceptance time. - Social providers need a service
- The form defaults to hiding social buttons; this page does not enable them. Pass
showSocialandonSocialSignupto show and connect GitHub and Google. That provider path does not run the email form’s field or terms checks, so handle any required consent in the provider flow as well.
<SignupForm
showSocial
onSocialSignup={startProviderSignup}
onSubmit={createAccount}
/>The callback names above represent functions supplied by your app. Keep server-side validation and credential handling in your authentication service. Adapt the form’s demo messages to that service’s actual result.
A closer look
About this block
This block separates page presentation from form interaction. Its Preact form owns input, validation, loading and feedback state, while your application supplies authentication and real destinations. Shared Kamod primitives provide the visual language and basic interactions.
Composition and customization
The copied page.tsx owns centering, background, branding and any cover image. Its sibling signup-form.tsx owns the form. Shared helpers in auth/shared supply validation and provider artwork; optional branding lives separately in shared/branding. You can reuse the form inside another page without copying the original page layout, but keep the form’s imported helpers. The form stores values internally; adding a value or initialValues prop requires changing that implementation. Change field names, payload types and validation together when adapting the form, and keep the service contract aligned with them.
Responsive behavior
A two-column page pairs a constrained form with a cover image. Below 1024px the image is hidden and the form takes the available width; branding remains above it.
Keep labels, validation text and legal links able to wrap. Test with the on-screen keyboard open and a long error message, not only an empty form. Light and dark colors follow your application’s Kamod theme. The full-page wrappers use min-h-svh, so they can grow when content needs more height. Preserve that flexibility instead of forcing a fixed height that clips error messages or the submit button.
Accessibility
- Labels and errors
- Preserve each field’s label,
aria-invalidand error association. Failed local validation focuses the first invalid field. If you add inputs, give them unique IDs and names, and include them in the error-focus logic. Multiple instances of the same copied form need distinct ID prefixes on labels, controls, help text and errors. A placeholder is not a replacement for a label. - Submission feedback
- Loading disables submission controls, and status messages use a polite live region. Preserve this feedback when connecting a real service. Replace demo wording with a clear result and a recovery action that does not expose sensitive account details.
Before shipping your adaptation, check 320px, 768px, 1024px and 1440px, keyboard-only operation, 200% zoom, both color modes and your longest real content. These checks apply to your finished integration; the demo cannot guarantee accessibility after its structure or behavior changes.
Replace the demo behavior
Remove the artificial sleep() delay and demoRejects() rule, replace the demo success/error messages, and supply real account and legal URLs. Local validation is feedback, not a security boundary. The backend must validate input and own account creation, sessions and recovery. Branded layouts also need a real home destination on KamodBrandLink, which otherwise points to #. If you replace the cover, review its alt text: use an empty alternative for purely decorative artwork and meaningful text only when it communicates useful information.
Keep building
Source and customization
Use the checked-in Kamod implementation as your reference when adapting this variant. The showcase’s Code tab includes its supporting files, and the setup instructions explain where to place them and how to keep their imports intact. Before changing a helper, trace where it is used in the composition and check its inputs in the Props and data reference. This helps you adapt one part of the block without overlooking the files or behavior it depends on.
Source: Signup 2 on GitHub
Keep your local copy focused on the layout and interactions your app actually uses. Start with the data and content, then adjust composition and styling using the same semantic theme tokens.
Ready to integrate? Follow Add this block and the local usage example.
Design inspiration
Design reference
This variant follows the corresponding signup design in the shadcn/ui collection. Use the reference to compare the registration column beside a larger cover area, with branding kept close to the form. Adapt the branding, copy and destinations to your product, then follow the usage example to connect the form’s callbacks to your authentication service.
This version uses Kamod’s Preact form components and theme tokens. The Kamod cover disappears below 1024px while the form and branding remain. Keep registration requirements and legal information in that persistent column rather than placing them in the artwork.
Inspect the original variant’s source for the reference composition. The variant details above describe this implementation’s behavior and integration boundaries.
Use the original as a design reference. To install the Preact version shown here, follow Add this block and use this page’s Code tab. Its files and imports are prepared for Kamod.