Emails

Typed email templates for Wander marketplace and WanderOS, built with React Email

Installation

pnpm add @wandercom/design-system-emails

Requires react and react-dom as peer dependencies. The package also installs @react-email/render, @react-email/components, and @react-email/tailwind as dependencies.

Usage

The package exports a single composeEmail function that renders any template to HTML and plain text:

import { composeEmail } from '@wandercom/design-system-emails';

const { html, text } = await composeEmail('os.transactional.guest.booking-confirmed', {
  propertyName: 'Desert Modern Retreat',
  propertyImageUrl: 'https://assets.wander.com/p/listing-hero.jpg',
  bookingDates: 'Mar 15 – Mar 20, 2026',
  bookingGuests: '2 guests',
  bookingUrl: 'https://example.com/bookings/abc123',
  unitAddress: '1234 Desert View Dr, Joshua Tree, CA 92252',
  mapsUrl: 'https://maps.google.com/?q=...',
  checkInInstructions: 'Lockbox code is 1234. Check-in after 4pm.',
  companyName: 'Desert Modern Retreat',
  unsubscribeUrl: 'https://example.com/unsubscribe',
  logoSrc: 'https://wander-ds.vercel.app/assets/wander-logomark.png',
  logoDarkSrc: 'https://wander-ds.vercel.app/assets/wander-logomark-dark.png',
});

The function is fully generic — TypeScript infers the correct props type from the template name, so you get autocomplete on both template keys and their required props.

Preview

Template keys

Templates use dot-notation keys: {domain}.{category}.{audience}.{template-name}

Marketplace transactional

Guest

TemplateKey
Booking cancelledmarketplace.transactional.guest.booking-cancelled
Email codemarketplace.transactional.guest.email-code
Payment chargedmarketplace.transactional.guest.payment-charged
Payment failedmarketplace.transactional.guest.payment-failed
Pre-reserve confirmedmarketplace.transactional.guest.pre-reserve-confirmed
Pre-reserve expiredmarketplace.transactional.guest.pre-reserve-expired
Pre-reserve launchedmarketplace.transactional.guest.pre-reserve-launched
Receiptmarketplace.transactional.guest.receipt
Trip confirmedmarketplace.transactional.guest.trip-confirmed
Upcoming paymentmarketplace.transactional.guest.upcoming-payment
Upcoming staymarketplace.transactional.guest.upcoming-stay
Verify identitymarketplace.transactional.guest.verify-identity

Operator

TemplateKey
Agreement signedmarketplace.transactional.operator.agreement-signed
Booking conflictmarketplace.transactional.operator.booking-conflict
Monthly statementmarketplace.transactional.operator.monthly-statement
New bookingmarketplace.transactional.operator.new-booking
New booking offermarketplace.transactional.operator.new-booking-offer
New reviewmarketplace.transactional.operator.new-review
Onboarding invitemarketplace.transactional.operator.onboarding-invite
Owner chat messagemarketplace.transactional.operator.owner-chat-message
Payout sentmarketplace.transactional.operator.payout-sent
Post-trip feedbackmarketplace.transactional.operator.post-trip-feedback
Sign agreementmarketplace.transactional.operator.sign-agreement
Upcoming bookingmarketplace.transactional.operator.upcoming-booking
W-9 approvedmarketplace.transactional.operator.w9-approved
W-9 receivedmarketplace.transactional.operator.w9-received
Weekly chat reportmarketplace.transactional.operator.weekly-chat-report

Services

TemplateKey
New task assignedmarketplace.transactional.services.new-task-assigned
Overdue taskmarketplace.transactional.services.overdue-task
Owner dashboard invitemarketplace.transactional.services.owner-dashboard-invite
Vendor portal invitemarketplace.transactional.services.vendor-portal-invite
Vendor task completedmarketplace.transactional.services.vendor-task-completed

WanderOS marketing

Guest

TemplateKey
Abandoned cartos.marketing.guest.abandoned-cart
Extend stayos.marketing.guest.extend-stay
Flash rebookos.marketing.guest.flash-rebook
Launch dayos.marketing.guest.launch-day
Stay againos.marketing.guest.stay-again
Welcomeos.marketing.guest.welcome

WanderOS transactional

Guest

TemplateKey
Booking cancelledos.transactional.guest.booking-cancelled
Booking confirmedos.transactional.guest.booking-confirmed
Booking updatedos.transactional.guest.booking-updated
Check-in dayos.transactional.guest.check-in-day
Payment failedos.transactional.guest.payment-failed
Payment method updatedos.transactional.guest.payment-method-updated
Payment receivedos.transactional.guest.payment-received
Pre-tripos.transactional.guest.pre-trip
RTB receivedos.transactional.guest.rtb-received
RTB rejectedos.transactional.guest.rtb-rejected
Upcoming paymentos.transactional.guest.upcoming-payment
Verify emailos.transactional.guest.verify-email

Operator

TemplateKey
Booking reviewos.transactional.operator.booking-review
Confirm domain removalos.transactional.operator.confirm-domain-removal
Confirm listing deletionos.transactional.operator.confirm-listing-deletion
Contact us formos.transactional.operator.contact-us-form
Custom domain activeos.transactional.operator.custom-domain-active
Custom domain expiringos.transactional.operator.custom-domain-expiring
Custom domain removedos.transactional.operator.custom-domain-removed
Email codeos.transactional.operator.email-code
Form submissionos.transactional.operator.form
List with us formos.transactional.operator.list-with-us-form
New bookingos.transactional.operator.new-booking
Org inviteos.transactional.operator.org-invite
PMS sync completeos.transactional.operator.pms-sync-complete
RTB receivedos.transactional.operator.rtb-received
Website auditos.transactional.operator.website-audit
Website liveos.transactional.operator.website-live

Generic

The generic template is a flexible template for ad-hoc emails that don't need a dedicated template:

const { html, text } = await composeEmail('generic', {
  heading: 'Your settings have been updated',
  body: [
    'We wanted to let you know that your account settings have been updated successfully.',
    'If you did not make this change, please contact our support team immediately.',
  ],
  buttonText: 'View settings',
  buttonHref: 'https://wander.com/settings',
  footer: 'wander',
  logoSrc: 'https://wander-ds.vercel.app/assets/wander-logomark.png',
  logoDarkSrc: 'https://wander-ds.vercel.app/assets/wander-logomark-dark.png',
});

The body prop accepts a single string or an array of strings (each rendered as a separate paragraph). The button only renders when both buttonText and buttonHref are provided.

Sending emails

The composeEmail function returns { html, text } — the rendered HTML and plain text versions. Pass these to any email provider:

import { composeEmail } from '@wandercom/design-system-emails';

const { html, text } = await composeEmail('os.transactional.guest.booking-confirmed', {
  // ... template props
});

await emailProvider.send({
  from: 'Wander <notifications@wander.com>',
  to: guest.email,
  subject: 'Booking confirmed',
  html,
  text,
});

Preview

To preview emails during development, run from the emails package:

cd packages/emails
pnpm dev

This starts the React Email dev server where you can browse and preview all templates with their default prop values.

Types

The package exports these types for use in consuming applications:

import type {
  EmailTemplateName,
  EmailTemplateMap,
  ComposeResult,
} from '@wandercom/design-system-emails';
  • EmailTemplateName — union of all template key strings
  • EmailTemplateMap — maps each template key to its required props
  • ComposeResult — { html: string; text: string }