Change Email Block
ChangeEmail is a controlled change-email form block built on MUI.
Installationโ
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-change-email-block
yarn add @nodeblocks/frontend-change-email-block
pnpm add @nodeblocks/frontend-change-email-block
bun add @nodeblocks/frontend-change-email-block
What You Needโ
| Item | Why it matters |
|---|---|
data | Single source of truth for form state (newEmail) |
onDataChange | Receives updated state + metadata on field changes |
currentEmail (optional) | Read-only current address shown above the new-email field |
errors (optional) | Displays field-level error feedback |
ChangeEmail does not own form state. Keep state in your app and pass it through data.
Code Examplesโ
- Quick Start
- Labels and URLs
- Compound Components
- Block Override
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} errors={Object.keys(errors).length ? errors : undefined} currentEmail="user@example.com" onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} /> ); }
Customize the current-email label, subcomponent copy, and validation errors.
function Example() { const defaultData = {newEmail: 'not-an-email'}; const [data, setData] = React.useState(defaultData); return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={{newEmail: 'Enter a valid email address.'}} onDataChange={setData} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > <ChangeEmail.Title>Update your email</ChangeEmail.Title> <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> </ChangeEmail> ); }
Pass labels.currentEmailDisplay on the root for the read-only email label. Override title, new-email field, and button copy via subcomponent props (see Compound Components tab). Defaults: title ใกใผใซใขใใฌในใๅคๆด, new email ๆฐใใใกใผใซใขใใฌใน / ๆฐใใใกใผใซใขใใฌในใๅ
ฅๅ, button ใกใผใซใขใใฌในใๅคๆด.
Use child blocks to customize layout while keeping controlled state behavior.
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > <ChangeEmail.Title>Update your email</ChangeEmail.Title> <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay currentEmail="user@example.com" /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> </ChangeEmail> ); }
Use function children to override default blocks and order.
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, customNotice: ( <div style={{ background: '#eef4ff', border: '1px solid #cddcff', borderRadius: '8px', padding: '12px', fontSize: '14px', marginBottom: 16, }} > We will send a confirmation link to your new address. </div> ), form: ( <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay currentEmail="user@example.com" /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> ), }, blockOrder: ['title', 'customNotice', ...defaultBlockOrder.filter(k => k !== 'title')], })} </ChangeEmail> ); }
When to use block overrides
Use overrides when you need to change order, replace default UI blocks, or inject custom content while preserving shared state handling. Default order is title, form.
Important Propsโ
Core Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
data | ChangeEmailFormData ({ newEmail? } or extended Record<string, unknown>) | Yes | - | Current form data object |
onDataChange | (data, meta) => void | Yes | - | Called on updates; meta includes name, value, cause (input, change, blur, clear, reset, programmatic), optional event |
errors | { [fieldName: string]: string | string[] } | No | undefined | Field errors keyed by data field name (e.g. newEmail) |
Content Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
currentEmail | string | No | undefined | Read-only current email address |
labels | { currentEmailDisplay?: string } | No | currentEmailDisplay: '็พๅจใฎใกใผใซใขใใฌใน' | Label for the current-email display only. Override title, field, and button copy through subcomponent props. |
Layout and Composition Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | No | undefined | Override default blocks or provide custom JSX children |
component | React.ElementType | No | 'form' | Root element type |
className | string | No | undefined | Class name for the root container |
sx | SxProps | No | undefined | MUI system styles for the root container |
ChangeEmail also inherits StackProps<'form'> (except children), so standard form props like onSubmit, id, and noValidate are supported. Default block order without overrides: title, form (defaultBlockOrder).
Default UI Blocksโ
| Block | Built on | Notes |
|---|---|---|
ChangeEmail (root) | Stack (component="form") | Controlled form container; inner content max width 684px |
ChangeEmail.Title | Typography | Title (variant="h6"); default ใกใผใซใขใใฌในใๅคๆด |
ChangeEmail.Form | Stack | Form fields container (submit handled by root form) |
ChangeEmail.Form.CurrentEmailDisplay | Typography + Stack | Read-only current email; label default ็พๅจใฎใกใผใซใขใใฌใน via labels.currentEmailDisplay and value from root currentEmail |
ChangeEmail.Form.NewEmailField | TextField | New email (name="newEmail", type="text", autoComplete="new-email", fullWidth); defaults ๆฐใใใกใผใซใขใใฌใน / ๆฐใใใกใผใซใขใใฌในใๅ
ฅๅ |
ChangeEmail.Form.ChangeEmailButton | Button | Submit (variant="contained", type="submit"); default ใกใผใซใขใใฌในใๅคๆด |
TypeScriptโ
import {ChangeEmail, ChangeEmailFormData} from '@nodeblocks/frontend-change-email-block';
type MyChangeEmailData = ChangeEmailFormData & {
confirmToken?: string;
};