> For the complete documentation index, see [llms.txt](https://help.tahua.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.tahua.io/product-news/2026/readme/email-preview.md).

# Email Preview

## Problem

Administrators editing system email templates had no way to see how an email would actually look when rendered with real data. This made it difficult to verify that template changes were correct before sending to recipients.

## How It Works

A **Preview Email** option is available in the dropdown menu next to each migrated system email on the Emails settings page. Clicking it opens a modal where an administrator can optionally select a specific funding application to use as preview context.

The preview page renders the email subject and body using the same variable-building logic the mailer uses at send time:

* **With a funding application selected**: the preview is populated with data from that application (applicant name, application title, milestones, etc.).
* **Without a selection**: only base variables (organisation name, sign-in links, etc.) are populated; email-specific variables that require application context are omitted.

## Mailer-Level Preview Definitions

The `define_email` macro registers an email and declares its preview requirements in one place, replacing a separate `register_email_content` call in the initializer. The block is the **single source of truth** for the email's variables — it is called both at preview time and by the mailer action itself via `email_variables_for`.

```ruby
define_email :milestones_created,
  category: :milestones,
  variables: %w[applicant_name organisation_name application_title milestone_count sign_in_link],
  mailer_type: FundingSystem::EmailTypes::EMAIL_TYPES[:applicant],
  preview_with: [:funding_application] do |organisation: nil, milestones: nil, user: nil, funding_application: nil, milestone_ids: nil, **|
    org = organisation || funding_application&.organisation || Organisation.current_organisation
    milestones ||= funding_application&.milestones || []
    h = ActionController::Base.helpers
    sign_in_link = h.link_to(org.term('sign_in_here'), Rails.application.routes.url_helpers.new_user_session_url(org.host_port))
    {
      applicant_name: user&.name || funding_application&.registration_entity&.name,
      organisation_name: org.name,
      application_title: funding_application&.title || I18n.t('shared.unknown_application'),
      milestone_count: milestones.count,
      sign_in_link: sign_in_link,
    }
  end
```

The mailer action calls `email_variables_for` to get its variables from the same block:

```ruby
def milestones_created(applicant_id, funding_application_id, *milestone_ids)
  # ...
  @email_variables = email_variables_for(:milestones_created,
    funding_application: @funding_application,
    organisation: @organisation,
    milestones: @milestones,
    milestone_ids: milestone_ids,
    user: @user,
  )
end
```

The `preview_with:` declaration tells the preview modal which objects are relevant for that email. The block must accept all kwargs that any caller might pass (preview or real send) and use nil-safe navigation for those not always present. Variables that return `nil` are simply omitted from the preview rather than shown as placeholders.

**Preview is only available for emails that have been migrated to `define_email`.** This ensures the preview uses exactly the same variable-building logic as the real send.

## UI Changes

* A **Preview Email** entry appears in the dropdown button on the Emails index page for emails migrated to `define_email` (desktop and mobile).
* Clicking Preview opens a selection modal where an optional funding application can be chosen. Submitting the modal opens the preview in a new tab and dismisses the modal.
* When Preview is triggered from the **email edit page**, the current unsaved subject and body are carried through to the preview — no save is required. TinyMCE content is synced automatically before the request is made. The Preview button is only shown on the edit page for migrated emails.
* When Preview is triggered from the **index page**, the saved content is used.
* The preview page shows the rendered subject and body inside the standard mailer layout.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.tahua.io/product-news/2026/readme/email-preview.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
