# Defaults: NotifyMe (/themes/sdk/reference/product/defaults-2)

```ts
import { … } from '@poltura/theme-sdk/product';
```

## `NotifyMe` [#notifyme]

"Notify me" for a variant that cannot be bought now: one email, sent once when it can be.

```ts
NotifyMe(props: NotifyMeProps): ReactElement | null
```

**Props:** [`NotifyMeProps`](/themes/sdk/reference/product/types-5#notifymeprops)

<Tab value='Example'>

```tsx
import { NotifyMe } from '@poltura/theme-sdk/product';

export function SoldOut({ variantId }: { variantId: string }) {
  return <NotifyMe variantId={variantId} reason='outOfStock' />;
}
```

</Tab>
<Tab value='Source'>

```tsx title="source/notify-me.tsx"
import { useId, type ReactElement } from 'react';
import { Loader2 } from 'lucide-react';
import { useTranslations } from '@poltura/theme-sdk/i18n';
import {
  useNotifyMe,
  type NotifyMeProps,
  type NotifyMeReason,
  type NotifyMeStatus,
} from '@poltura/theme-sdk/product';
import { Button, Checkbox, Input, Label, cn } from '@poltura/theme-sdk/ui';

const REASON_ATTRIBUTES = {
  comingSoon: 'coming_soon',
  preorderFull: 'preorder_full',
  outOfStock: 'out_of_stock',
} as const satisfies Record<NotifyMeReason, string>;

/** Sending reads as idle in `data-notify-me-state`, as the form shows it. */
function stateAttribute(status: NotifyMeStatus): string {
  return status === 'submitting' ? 'idle' : status;
}

function NotifyMeForm({ variantId, reason, className, copy }: NotifyMeProps): ReactElement | null {
  const t = useTranslations('preorder.notify');
  const id = useId();
  const notify = useNotifyMe(variantId);
  if (!notify.enabled) return null;
  const sending = notify.status === 'submitting';
  const invalid = notify.status === 'invalid';
  const emailId = `${id}-email`;
  const errorId = `${id}-error`;
  const announcement =
    notify.status === 'done'
      ? notify.confirmationSent
        ? t('confirmSent')
        : (copy?.success ?? t('success'))
      : (notify.error ?? '');

  return (
    <section
      className={cn('space-y-3 rounded-lg border p-4', className)}
      aria-labelledby={`${id}-title`}
      data-notify-me=''
      data-notify-me-reason={REASON_ATTRIBUTES[reason]}
      data-notify-me-state={stateAttribute(notify.status)}
    >
      <div className='space-y-1'>
        <h3 id={`${id}-title`} className='text-sm font-semibold'>
          {copy?.title ?? t('title')}
        </h3>
        <p className='text-sm text-muted-foreground'>{copy?.description ?? t('description')}</p>
      </div>
      {notify.status === 'done' ? null : (
        <form
          className='space-y-3'
          noValidate
          onSubmit={(event) => {
            event.preventDefault();
            void notify.submit().then((status) => {
              if (status === 'invalid') document.getElementById(emailId)?.focus();
            });
          }}
        >
          <div className='space-y-1.5'>
            <Label htmlFor={emailId}>{copy?.emailLabel ?? t('emailLabel')}</Label>
            <div className='flex flex-col gap-2 sm:flex-row'>
              <Input
                id={emailId}
                type='email'
                name='email'
                inputMode='email'
                autoComplete='email'
                maxLength={320}
                value={notify.email}
                placeholder={copy?.emailPlaceholder ?? t('emailPlaceholder')}
                aria-invalid={invalid}
                aria-describedby={invalid ? errorId : undefined}
                onChange={(event) => notify.setEmail(event.currentTarget.value)}
              />
              <Button type='submit' disabled={sending} className='shrink-0'>
                {sending ? <Loader2 className='mr-2 h-4 w-4 animate-spin' aria-hidden /> : null}
                {sending ? (copy?.submitting ?? t('submitting')) : (copy?.submit ?? t('submit'))}
              </Button>
            </div>
            {invalid ? (
              <p id={errorId} role='alert' className='text-sm text-destructive'>
                {t('invalidEmail')}
              </p>
            ) : null}
          </div>
          <div className='flex items-start gap-2'>
            <Checkbox
              id={`${id}-newsletter`}
              checked={notify.newsletter}
              onCheckedChange={(checked) => notify.setNewsletter(checked)}
              data-notify-me-newsletter=''
            />
            <Label htmlFor={`${id}-newsletter`} className='font-normal leading-snug'>
              {notify.consent}
            </Label>
          </div>
          <p className='text-xs text-muted-foreground'>
            {notify.newsletter ? t('privacyOptIn') : (copy?.privacy ?? t('privacy'))}
          </p>
        </form>
      )}
      <output
        aria-live='polite'
        data-notify-me-result=''
        className={cn(
          'block text-sm empty:hidden',
          notify.status === 'error' ? 'text-destructive' : 'text-foreground',
        )}
      >
        {announcement}
      </output>
    </section>
  );
}

export function NotifyMe(props: NotifyMeProps): ReactElement | null {
  return <NotifyMeForm key={props.variantId} {...props} />;
}
```

</Tab>


**See also:** [`useNotifyMe`](/themes/sdk/reference/product/hooks-1#usenotifyme)
