Accept Invitation Block
AcceptInvitation is a controlled invitation acceptance form block built on MUI.
Installationโ
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-accept-invitation-block
yarn add @nodeblocks/frontend-accept-invitation-block
pnpm add @nodeblocks/frontend-accept-invitation-block
bun add @nodeblocks/frontend-accept-invitation-block
What You Needโ
| Item | Why it matters |
|---|---|
data | Single source of truth for form state (name, password, confirmPassword) |
onDataChange | Receives updated state + metadata on field changes |
email (optional) | Read-only invited email shown above the form |
errors (optional) | Displays field-level error feedback |
AcceptInvitation 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 = { name: '', password: '', confirmPassword: '', }; 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') { nextErrors = {...restErrors}; if (meta.name === 'name' && !String(nextData.name || '').trim()) { nextErrors.name = 'Name is required.'; } if (meta.name === 'password' && !nextData.password) { nextErrors.password = 'Password is required.'; } if (meta.name === 'confirmPassword' && !nextData.confirmPassword) { nextErrors.confirmPassword = 'Please confirm your password.'; } } setErrors(nextErrors); setData(nextData); }; return ( <AcceptInvitation data={data} email="invited@example.com" errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} /> ); }
Customize labels, placeholders, invited email, and validation errors.
function Example() { const defaultData = { name: '', password: 'short', confirmPassword: 'nomatch', }; const [data, setData] = React.useState(defaultData); return ( <AcceptInvitation data={data} email="invited@example.com" labels={{ acceptInvitationTitle: 'Create your account', emailDisplay: 'Invited email', nameField: 'Full name', passwordField: 'Password', confirmPasswordField: 'Confirm password', acceptInvitationButton: 'Complete setup', }} placeholders={{ nameField: 'Enter your name', passwordField: 'Choose a password', confirmPasswordField: 'Re-enter your password', }} errors={{ name: 'Name is required.', password: 'Use at least 8 characters.', confirmPassword: 'Passwords do not match.', }} onDataChange={setData} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} /> ); }
Pass labels and placeholders on the root for copy overrides, or set them on individual subcomponents (see Compound Components tab).
Use child blocks to customize layout while keeping controlled state behavior.
function Example() { const defaultData = { name: '', password: '', confirmPassword: '', }; 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') { nextErrors = {...restErrors}; if (meta.name === 'name' && !String(nextData.name || '').trim()) { nextErrors.name = 'Name is required.'; } if (meta.name === 'password' && !nextData.password) { nextErrors.password = 'Password is required.'; } if (meta.name === 'confirmPassword' && !nextData.confirmPassword) { nextErrors.confirmPassword = 'Please confirm your password.'; } } setErrors(nextErrors); setData(nextData); }; return ( <AcceptInvitation data={data} email="invited@example.com" errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > <AcceptInvitation.AcceptInvitationTitle>Create your account</AcceptInvitation.AcceptInvitationTitle> <AcceptInvitation.Form> <AcceptInvitation.Form.EmailDisplay email="invited@example.com" /> <AcceptInvitation.Form.NameField label="Full name" placeholder="Enter your name" /> <AcceptInvitation.Form.PasswordField label="Password" placeholder="Choose a password" /> <AcceptInvitation.Form.ConfirmPasswordField label="Confirm password" placeholder="Re-enter your password" /> <AcceptInvitation.Form.AcceptInvitationButton>Complete setup</AcceptInvitation.Form.AcceptInvitationButton> </AcceptInvitation.Form> </AcceptInvitation> ); }
Use function children to override default blocks and order.
function Example() { const defaultData = { name: '', password: '', confirmPassword: '', }; 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') { nextErrors = {...restErrors}; if (meta.name === 'name' && !String(nextData.name || '').trim()) { nextErrors.name = 'Name is required.'; } if (meta.name === 'password' && !nextData.password) { nextErrors.password = 'Password is required.'; } if (meta.name === 'confirmPassword' && !nextData.confirmPassword) { nextErrors.confirmPassword = 'Please confirm your password.'; } } setErrors(nextErrors); setData(nextData); }; return ( <AcceptInvitation data={data} email="invited@example.com" 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, }} > You were invited to join. Set your name and password below. </div> ), form: ( <AcceptInvitation.Form> <AcceptInvitation.Form.EmailDisplay email="invited@example.com" /> <AcceptInvitation.Form.NameField label="Full name" placeholder="Enter your name" /> <AcceptInvitation.Form.PasswordField label="Password" placeholder="Choose a password" /> <AcceptInvitation.Form.ConfirmPasswordField label="Confirm password" placeholder="Re-enter your password" /> <AcceptInvitation.Form.AcceptInvitationButton> Complete setup </AcceptInvitation.Form.AcceptInvitationButton> </AcceptInvitation.Form> ), }, blockOrder: [ 'acceptInvitationTitle', 'customNotice', ...defaultBlockOrder.filter(k => k !== 'acceptInvitationTitle'), ], })} </AcceptInvitation> ); }
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 acceptInvitationTitle, form.
Important Propsโ
Core Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
data | AcceptInvitationFormData ({ name, password, confirmPassword } 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 (name, password, confirmPassword) |
Content Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | No | undefined | Read-only invited email address |
labels | { acceptInvitationTitle?, emailDisplay?, nameField?, passwordField?, confirmPasswordField?, acceptInvitationButton? } | No | acceptInvitationTitle: 'ใขใซใฆใณใไฝๆ', emailDisplay: 'ใกใผใซใขใใฌใน', nameField: 'ๆฐๅ', passwordField: 'ใในใฏใผใ', confirmPasswordField: 'ใในใฏใผใๅๅ
ฅๅ', acceptInvitationButton: 'ใขใซใฆใณใใไฝๆ' | Override default text labels |
placeholders | { nameField?, passwordField?, confirmPasswordField? } | No | nameField: 'ๆฐๅใๅ
ฅๅ', passwordField: 'ใในใฏใผใใๅ
ฅๅ', confirmPasswordField: 'ใในใฏใผใใๅๅ
ฅๅ' | Override field placeholders |
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 |
AcceptInvitation also inherits StackProps<'form'> (except children), so standard form props like onSubmit, id, and noValidate are supported. Default block order without overrides: acceptInvitationTitle, form (defaultBlockOrder).
Default UI Blocksโ
| Block | Built on | Notes |
|---|---|---|
AcceptInvitation (root) | Stack (component="form") | Controlled form container; default max width 496px |
AcceptInvitation.AcceptInvitationTitle | Typography | Title text block; default ใขใซใฆใณใไฝๆ |
AcceptInvitation.Form | Stack | Form fields container (submit handled by root form) |
AcceptInvitation.Form.EmailDisplay | Typography + Stack | Read-only invited email display; label default ใกใผใซใขใใฌใน; uses root email prop |
AcceptInvitation.Form.NameField | TextField | name="name"; required; autoComplete="name"; default ๆฐๅ / ๆฐๅใๅ
ฅๅ |
AcceptInvitation.Form.PasswordField | TextField + IconButton | name="password"; required; autoComplete="new-password"; default ใในใฏใผใ / ใในใฏใผใใๅ
ฅๅ; visibility toggle |
AcceptInvitation.Form.ConfirmPasswordField | TextField + IconButton | name="confirmPassword"; required; autoComplete="new-password"; default ใในใฏใผใๅๅ
ฅๅ / ใในใฏใผใใๅๅ
ฅๅ; visibility toggle |
AcceptInvitation.Form.AcceptInvitationButton | Button | Submit button (variant="contained", size="large", fullWidth, type="submit"); default ใขใซใฆใณใใไฝๆ |
TypeScriptโ
import {AcceptInvitation, AcceptInvitationFormData} from '@nodeblocks/frontend-accept-invitation-block';
type MyAcceptInvitationData = AcceptInvitationFormData & {
inviteToken?: string;
};