react-email-rails 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05b64507c82feabb25781eaec8487c5e7282f65060abbac88719bfb2bc79d499
4
- data.tar.gz: fc1f7fe4d40195537b5218818aa0fb8b72be78834238223dc377c52f7843af15
3
+ metadata.gz: 58d96423ee9be99d650a3ad7d164cbb8aca228ccbcc2b36d1a9be2bac2e06172
4
+ data.tar.gz: 370303cb64b83fd747009f59291ddf5f4598bd5670651ed89ec23edcf4b1072e
5
5
  SHA512:
6
- metadata.gz: 61afe5bf3fc51e550ca9608bd91deeeb9794cd4a90bac53a7bf0fb48f2f12f5984f50dadbbd245c05e34d8dc4ab7c478f2ff8420aa94e9f57c98e39fb04bab01
7
- data.tar.gz: 992fd9253d2a2cf31f9ecd37f7bd960c52efddaf74541aefa5ed124ebef4941d9404c00d7f417a6cc2d36f998d3391d8f1bf3ec3d865ec232c43c5a2df2ed4c1
6
+ metadata.gz: ccee408e14a4f0b831ae58d19ce6891687b273a9f017e6b97184bdbeef7a79107a4e7ba32b4e5db551c4160e2a85b62509c9c85e1ee53bca394b77857af4e2c9
7
+ data.tar.gz: 262e70dd3acaf9b6bd0e8d1854649bb4a475634c4b0569c69dd2498f3a8bd9093bc33e89e35fe2c6785315247838122d081e4a26206c73d8d51da97698aa31a3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ - Every `react:` email now receives `mailer` and `message` props, mirroring Action Mailer's `mailer`/`message` ERB view helpers. Rendering now happens after Action Mailer assigns headers, so `message` includes subject, addressing, and default `from`/`reply_to` values. Per-mail and shared props win on conflict, serializers whose `as_json` returns a Hash receive the context, and collection props keep their original shape. The npm package exports matching `Mailer`/`Message` TypeScript types.
6
+
7
+ ## 0.5.0
8
+
9
+ - Add `react_email_share` for sharing props across every `react:` email a mailer renders, mirroring inertia-rails' `inertia_share`. Supports static values, lazy lambdas and blocks (evaluated in the mailer instance), `only`/`except`/`if`/`unless` filters, and subclass inheritance. Per-mail props win over shared props.
10
+ - Shared props merge shallowly by default. Pass `deep_merge: true` to `mail` to merge nested hashes, or set `config.deep_merge_shared_props = true` to make it the default.
11
+
3
12
  ## 0.4.1
4
13
 
5
14
  - `ReactEmailRails.parse` now neutralizes unsafe link/button URI schemes: hrefs whose scheme is not `http`, `https`, `mailto`, or `tel` (e.g. `javascript:`/`data:`) are blanked before they reach the document `Hash`. Scheme detection ignores the whitespace and control characters browsers strip when resolving a scheme, so case- and whitespace-obfuscated payloads are caught too.
data/README.md CHANGED
@@ -2,15 +2,36 @@
2
2
 
3
3
  # react-email-rails
4
4
 
5
- Build and send emails using React and Rails with [React Email](https://react.email) and [Action Mailer](https://guides.rubyonrails.org/action_mailer_basics.html).
5
+ Build and send Action Mailer emails with [React Email](https://react.email), TypeScript, and Vite.
6
+
7
+ react-email-rails lets Rails render React Email components into HTML and plain text, then deliver them through the Action Mailer stack you already know: previews, headers, callbacks, queues, instrumentation, and delivery all keep working normally.
8
+
9
+ ## Why
10
+
11
+ HTML email is still awkward. React Email gives you a nicer component model, email-safe primitives, Tailwind support, and TypeScript. This gem connects that workflow to Rails without replacing Action Mailer.
12
+
13
+ You get:
14
+
15
+ - React Email components rendered from `mail(...)`
16
+ - HTML and plain-text output from the same component
17
+ - Rails mailer previews, tests, queues, callbacks, and delivery
18
+ - Vite-powered development rendering
19
+ - A production renderer bundle built during `assets:precompile`
20
+ - Optional persistent rendering for high-volume workers
21
+ - Optional server-side rendering for `@react-email/editor` documents
22
+
23
+ ## Status
24
+
25
+ react-email-rails is pre-1.0. It was extracted from [XOXO](https://xoxo.email), which is pre-launch, so the API may still change and it has not yet been battle-tested in high-volume production.
26
+
27
+ The supported Ruby, Rails, Node, React, and Vite versions are tested in CI. Please [open an issue](https://github.com/heysupertape/react-email-rails/issues) for bugs, rough edges, or integration feedback.
6
28
 
7
29
  ## Contents
8
30
 
9
- - [Why](#why)
10
- - [How](#how)
11
- - [Status](#status)
12
31
  - [Requirements](#requirements)
13
- - [Quick Start](#quick-start)
32
+ - [Installation](#installation)
33
+ - [Your First Email](#your-first-email)
34
+ - [How Rendering Works](#how-rendering-works)
14
35
  - [Usage](#usage)
15
36
  - [Configuration](#configuration)
16
37
  - [Deployment](#deployment)
@@ -19,73 +40,58 @@ Build and send emails using React and Rails with [React Email](https://react.ema
19
40
  - [Security](#security)
20
41
  - [License](#license)
21
42
 
22
- ## Why
23
-
24
- Building HTML emails is painfully archaic. [React Email](https://react.email) brings React, Tailwind, and TypeScript to email templates. This gem wires it into Action Mailer so React components deliver as generated HTML and text emails.
25
-
26
- ## How
27
-
28
- **In development,** the gem renders components live through Vite's dev pipeline, so your emails get the same module resolution and transforms as the rest of your frontend.
29
-
30
- **In production,** Rails builds a server-side renderer bundle during `assets:precompile` using `reactEmailRails()` in your Vite config for discovery and options.
31
-
32
- react-email-rails automatically renders both HTML and plain-text versions from the same component. Delivery, headers, previews, queues, and callbacks all stay normal Action Mailer. If rendering fails, no email is sent and `ReactEmailRails::RenderError` is raised.
33
-
34
- ## Status
35
-
36
- **react-email-rails is pre-1.0.** It's tested in CI across the supported Ruby, Rails, Node, and Vite versions, but it hasn't been battle-tested in high-volume production environments yet, and the API may still change before 1.0. Please [share feedback and report issues](https://github.com/heysupertape/react-email-rails/issues).
37
-
38
43
  ## Requirements
39
44
 
40
- - Ruby >= 3.3
41
- - Action Mailer, Active Support, and Railties >= 7.1 and < 9.0
42
- - Node >= 20.19
43
- - Vite 7 or 8
44
- - React 18 or 19
45
- - `@react-email/render` 2.x
45
+ | Dependency | Version |
46
+ |------------|---------|
47
+ | Ruby | >= 3.3 |
48
+ | Rails | Action Mailer, Active Support, and Railties >= 7.1 and < 9.0 |
49
+ | Node | >= 20.19 |
50
+ | Vite | 7 or 8 |
51
+ | React | 18 or 19 |
52
+ | `@react-email/render` | 2.x |
46
53
 
47
- > We recommend [rails_vite](https://github.com/skryukov/rails_vite/) for Vite with Rails.
54
+ We recommend [rails_vite](https://github.com/skryukov/rails_vite/) for Vite in Rails apps.
48
55
 
49
- ## Quick Start
56
+ ## Installation
50
57
 
51
- Add the gem:
58
+ Add the Ruby gem:
52
59
 
53
60
  ```ruby
54
61
  # Gemfile
55
-
56
62
  gem "react-email-rails"
57
63
  ```
58
64
 
59
- ### Automatic Install
60
-
61
- Run the installer:
65
+ Then install it with Rails:
62
66
 
63
67
  ```sh
68
+ bundle install
64
69
  bin/rails generate react_email_rails:install
65
70
  ```
66
71
 
67
- This creates `config/initializers/react_email_rails.rb`, installs missing JavaScript dependencies when it can detect your package manager, adds `reactEmailRails()` to `vite.config.*`, and creates `app/javascript/emails`.
72
+ The installer creates `config/initializers/react_email_rails.rb`, installs missing JavaScript dependencies when it can detect your package manager, adds `reactEmailRails()` to `vite.config.*`, and creates `app/javascript/emails`.
68
73
 
69
- The installed setup then follows the normal Rails lifecycle:
74
+ After installation, the normal Rails flow applies:
70
75
 
71
- - `bin/rails generate react_email_rails:email ...` creates matching mailers and React components.
72
- - `bin/dev` renders through Vite on demand.
73
- - `bin/rails assets:precompile` builds the production renderer bundle automatically.
74
- - `bin/rails react_email_rails:build` builds the bundle directly when CI or tests need it.
76
+ - Generate mailers and components with `bin/rails generate react_email_rails:email ...`.
77
+ - Run `bin/dev` in development; email components render through Vite on demand.
78
+ - Run `bin/rails assets:precompile` for production; the renderer bundle builds automatically.
79
+ - Run `bin/rails react_email_rails:build` directly when CI or tests need the bundle without the full asset task.
75
80
 
76
- ### Manual Install
81
+ ### Manual Setup
77
82
 
78
- Install the npm package and peer dependencies manually:
83
+ If you prefer to wire things up yourself, install the npm package and React Email dependencies:
79
84
 
80
85
  ```sh
81
86
  npm i react-email-rails @react-email/render @react-email/components react react-dom
82
87
  ```
83
88
 
84
- Update your Vite config to add the plugin:
89
+ Use the equivalent command for pnpm, Yarn, or Bun if your app uses a different package manager.
90
+
91
+ Then add the Vite plugin:
85
92
 
86
93
  ```ts
87
94
  // vite.config.ts
88
-
89
95
  import { defineConfig } from "vite"
90
96
  import { reactEmailRails } from "react-email-rails"
91
97
 
@@ -94,7 +100,7 @@ export default defineConfig({
94
100
  })
95
101
  ```
96
102
 
97
- ### Your First Email
103
+ ## Your First Email
98
104
 
99
105
  Generate a mailer and React Email component:
100
106
 
@@ -102,13 +108,15 @@ Generate a mailer and React Email component:
102
108
  bin/rails generate react_email_rails:email Account welcome
103
109
  ```
104
110
 
105
- The generator follows Rails' mailer generator shape: `NAME [method method]`. It creates the mailer, matching React components, a mailer preview, and a test. It reads `emails.path` and `emails.extension` from `reactEmailRails()` when available. Pass flags to override them:
111
+ The generator follows Rails' mailer generator shape: `NAME [method method]`. It creates a mailer, React component, mailer preview, and test. It also reads `emails.path` and `emails.extension` from `reactEmailRails()` when available.
112
+
113
+ Pass flags when you need to override the detected component directory or extension:
106
114
 
107
115
  ```sh
108
116
  bin/rails generate react_email_rails:email Account welcome --emails-path=app/emails --extension=jsx
109
117
  ```
110
118
 
111
- Edit the generated mailer to pass any necessary props:
119
+ Update the generated mailer to pass props into the React component:
112
120
 
113
121
  ```ruby
114
122
  class AccountMailer < ApplicationMailer
@@ -128,11 +136,10 @@ class AccountMailer < ApplicationMailer
128
136
  end
129
137
  ```
130
138
 
131
- Edit the generated email component:
139
+ Then edit the generated component:
132
140
 
133
141
  ```tsx
134
142
  // app/javascript/emails/account_mailer/welcome.tsx
135
-
136
143
  import { Body, Container, Html, Text } from "@react-email/components"
137
144
 
138
145
  type WelcomeProps = {
@@ -154,27 +161,70 @@ export default function Welcome({ account }: WelcomeProps) {
154
161
  }
155
162
  ```
156
163
 
157
- > [@react-email/components](https://react.email/docs/components/html) provides primitives like `<Button>`, `<Heading>`, `<Tailwind>`, and more.
164
+ Deliver it like any other Action Mailer email:
165
+
166
+ ```ruby
167
+ AccountMailer.with(account: current_account).welcome.deliver_later
168
+ ```
169
+
170
+ React Email also provides primitives like [`<Button>`, `<Heading>`, `<Tailwind>`, and more](https://react.email/docs/components/html).
171
+
172
+ ## How Rendering Works
173
+
174
+ In development, react-email-rails renders components through Vite's dev pipeline. Your email components get the same module resolution and transforms as the rest of your frontend.
175
+
176
+ In production, `assets:precompile` builds a server-side renderer bundle from your Vite config. Rails runs that bundle with Node whenever an email needs to render.
158
177
 
159
- That's it. It now renders and delivers like any other Action Mailer email.
178
+ Every `react:` email renders HTML and plain text from the same component. If rendering fails, the email is not sent and `ReactEmailRails::RenderError` is raised.
160
179
 
161
180
  ## Usage
162
181
 
163
- Pass data from your mailer. Each top-level key becomes a prop on the component's default export.
182
+ ### Passing Props
183
+
184
+ Top-level keys passed to `react:` become props on the component's default export. The API is intentionally close to [inertia-rails](https://inertia-rails.dev), so apps using both libraries should feel consistent.
164
185
 
165
186
  ```ruby
166
- mail react: { foo: "bar" }, ...
187
+ mail(
188
+ to: account.email,
189
+ subject: "Welcome",
190
+ react: {
191
+ account: {
192
+ name: account.name,
193
+ },
194
+ },
195
+ )
167
196
  ```
168
197
 
169
198
  ```tsx
170
- export default function Email({ foo }: { foo: string }) {
199
+ type WelcomeProps = {
200
+ account: {
201
+ name: string
202
+ }
203
+ }
204
+
205
+ export default function Welcome({ account }: WelcomeProps) {
171
206
  // ...
172
207
  }
173
208
  ```
174
209
 
175
- Choose the level of inference you want:
210
+ ### Component Inference
211
+
212
+ By default, react-email-rails infers the component from the mailer and action:
213
+
214
+ | Mailer action | Component |
215
+ |---------------|-----------|
216
+ | `AccountMailer#welcome` | `account_mailer/welcome` |
217
+ | `Users::InviteMailer#new_invite` | `users/invite_mailer/new_invite` |
176
218
 
177
- ### Implicit Component, Instance Props
219
+ `AccountMailer#welcome` resolves to `app/javascript/emails/account_mailer/welcome.tsx` or `.jsx` with the default Vite options.
220
+
221
+ Use `react: true` to render the inferred component. By default it renders with no props, which is handy for emails that take none:
222
+
223
+ ```ruby
224
+ mail(to: account.email, subject: "Welcome", react: true)
225
+ ```
226
+
227
+ To have `react: true` build props automatically, enable `use_react_instance_props`. The component then receives the mailer's instance variables as props:
178
228
 
179
229
  ```ruby
180
230
  class AccountMailer < ApplicationMailer
@@ -182,21 +232,24 @@ class AccountMailer < ApplicationMailer
182
232
 
183
233
  def welcome
184
234
  @account = params.fetch(:account)
185
- mail react: true, to: @account.email, subject: "Welcome"
235
+
236
+ mail(to: @account.email, subject: "Welcome", react: true)
186
237
  end
187
238
  end
188
239
  ```
189
240
 
190
- Action Mailer's framework assigns (including `params` and `rendered_format`) are excluded from instance props.
241
+ Action Mailer's framework assigns, including `params` and `rendered_format`, are excluded from instance props.
191
242
 
192
- Without `use_react_instance_props`, `react: true` still infers the component and renders it with no props, which is handy for emails that take no props at all.
243
+ ### Explicit Components
193
244
 
194
- ### Implicit Component, Explicit Props
245
+ Pass a component name when the mailer action and component path do not line up:
195
246
 
196
247
  ```ruby
197
248
  mail(
198
- ...
199
- react: {
249
+ to: account.email,
250
+ subject: "Welcome",
251
+ react: "accounts/welcome",
252
+ props: {
200
253
  account: {
201
254
  name: account.name,
202
255
  },
@@ -204,58 +257,168 @@ mail(
204
257
  )
205
258
  ```
206
259
 
207
- ### Explicit Component, Explicit Props
260
+ To change naming globally, override `component_path_resolver` in your [Rails configuration](#rails-configuration).
261
+
262
+ ### Shared Props
263
+
264
+ Use `react_email_share` to merge props into every `react:` email for a mailer and its subclasses:
265
+
266
+ ```ruby
267
+ class MarketingMailer < ApplicationMailer
268
+ react_email_share app_name: "Acme"
269
+
270
+ react_email_share unread_count: -> { params[:account]&.unread_count }
271
+
272
+ react_email_share do
273
+ { brand: { name: "Acme", url: marketing_url } }
274
+ end
275
+ end
276
+ ```
277
+
278
+ Per-mail props win over shared props of the same name:
208
279
 
209
280
  ```ruby
210
281
  mail(
211
- ...
212
- react: "accounts/welcome",
213
- props: {
214
- account: {
215
- name: account.name,
216
- },
282
+ to: account.email,
283
+ subject: "Welcome",
284
+ react: {
285
+ app_name: "Acme Pro",
217
286
  },
218
287
  )
219
288
  ```
220
289
 
221
- These forms mirror [inertia-rails](https://inertia-rails.dev), making the two libraries feel consistent when used together.
290
+ Shared props work with `react:` hashes, `react: true`, and explicit `react: "component", props: ...` calls.
222
291
 
223
- ### Component Names
292
+ ### Conditional Shared Props
224
293
 
225
- Component names are inferred from the mailer and action:
294
+ `react_email_share` accepts the same filter options as `before_action`: `only`, `except`, `if`, and `unless`.
226
295
 
227
- | Mailer action | Component |
228
- |---------------|-----------|
229
- | `AccountMailer#welcome` | `account_mailer/welcome` |
230
- | `Users::InviteMailer#new_invite` | `users/invite_mailer/new_invite` |
296
+ ```ruby
297
+ react_email_share only: [:welcome, :reactivation] do
298
+ { promotion: params.fetch(:promotion) }
299
+ end
231
300
 
232
- Rails derives `account_mailer` from `AccountMailer` via `mailer_name`. By default, `account_mailer/welcome` resolves to `app/javascript/emails/account_mailer/welcome.tsx` or `.jsx`.
301
+ react_email_share if: :account_active? do
302
+ { account: { name: params.fetch(:account).name } }
303
+ end
304
+ ```
305
+
306
+ You can also share props inside an action before calling `mail`:
307
+
308
+ ```ruby
309
+ def welcome
310
+ account = params.fetch(:account)
311
+
312
+ react_email_share notice: "Thanks for joining!"
313
+
314
+ mail(
315
+ to: account.email,
316
+ subject: "Welcome",
317
+ react: { account: account.as_json(only: [:name]) },
318
+ )
319
+ end
320
+ ```
321
+
322
+ ### Deep Merging Shared Props
233
323
 
234
- Files and directories starting with `_` are ignored as renderable email entries by default. Use them for shared components such as `_components/email_layout.tsx`. They can still be imported by email components.
324
+ Shared props are merged shallowly by default. That means a per-mail prop replaces a shared prop with the same name.
325
+
326
+ Pass `deep_merge: true` to merge nested hashes instead:
327
+
328
+ ```ruby
329
+ react_email_share do
330
+ { settings: { theme: "light", locale: "en" } }
331
+ end
332
+
333
+ mail(
334
+ to: account.email,
335
+ subject: "Welcome",
336
+ react: {
337
+ settings: {
338
+ theme: "dark",
339
+ },
340
+ },
341
+ deep_merge: true,
342
+ )
343
+ ```
235
344
 
236
- Override the inferred name per mail:
345
+ The component receives:
237
346
 
238
347
  ```ruby
239
- mail react: "users/welcome", props: { user: }, to:, subject:
348
+ { settings: { theme: "dark", locale: "en" } }
349
+ ```
350
+
351
+ Set `config.deep_merge_shared_props = true` to make deep merging the default for every email.
352
+
353
+ ### Mailer and Message Props
354
+
355
+ Every `react:` email receives `mailer` and `message` props, mirroring the [`mailer` and `message` view helpers](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-view-helpers) available to Action Mailer ERB views.
356
+
357
+ `mailer` identifies the mailer action. `message` reflects the email after Action Mailer has assigned headers and defaults, including default `from` and `reply_to` values. With the default prop transform, import the `Mailer` and `Message` types to annotate them:
358
+
359
+ ```tsx
360
+ import type { Mailer, Message } from "react-email-rails"
361
+ import { Body, Container, Html, Text } from "@react-email/components"
362
+
363
+ type WelcomeProps = {
364
+ account: { name: string }
365
+ mailer: Mailer
366
+ message: Message
367
+ }
368
+
369
+ export default function Welcome({ account, mailer, message }: WelcomeProps) {
370
+ return (
371
+ <Html>
372
+ <Body>
373
+ <Container>
374
+ <Text>Welcome, {account.name}</Text>
375
+ <Text>Re: {message.subject}</Text>
376
+ </Container>
377
+ </Body>
378
+ </Html>
379
+ )
380
+ }
240
381
  ```
241
382
 
242
- Or override `component_path_resolver` globally in your [configuration](#configuration).
383
+ | Prop | Example |
384
+ |------|---------|
385
+ | `mailer.mailerName` | `"account_mailer"` |
386
+ | `mailer.actionName` | `"welcome"` |
387
+ | `message.subject` | `"Welcome"` |
388
+ | `message.to` | `["account@example.com"]` |
389
+ | `message.cc`, `message.bcc` | `["…"]` or `null` |
390
+ | `message.from`, `message.replyTo` | `["app@example.com"]` |
391
+
392
+ Context is merged before prop serialization, so keys follow `config.transform_props` just like your own props. The exported TypeScript types describe the default `:lower_camel` shape.
393
+
394
+ Per-mail and shared props win on conflict, so a prop named `mailer` or `message` overrides the injected context. Serializer props receive the context when `as_json` returns a hash. Collections, arrays, and other non-object values pass through unchanged so their top-level shape is preserved.
243
395
 
244
396
  ### Prop Serialization
245
397
 
246
- Like `render json:`, `mail react:` accepts any object that responds to `as_json`, including hashes, Active Model objects, and serializers such as [Alba](https://github.com/okuramasafumi/alba) or [ActiveModel::Serializer](https://github.com/rails-api/active_model_serializers).
398
+ Props are serialized with `as_json`, just like `render json:`. You can pass hashes, arrays, Active Model objects, and serializer output from libraries such as [Alba](https://github.com/okuramasafumi/alba) or [ActiveModel::Serializer](https://github.com/rails-api/active_model_serializers).
247
399
 
248
- ### Prop Transformation
400
+ Prop keys are camelized by default, so `plan_name` arrives in React as `planName`. See [Prop Transformation](#prop-transformation) to change that behavior.
401
+
402
+ ### Component Files
249
403
 
250
- Prop keys are camelized by default, so `account.plan_name` arrives as `account.planName`. Override `transform_props` in your [configuration](#configuration).
404
+ Files and directories starting with `_` are ignored as renderable email entries by default. Use them for shared components, layouts, and helpers:
405
+
406
+ ```text
407
+ app/javascript/emails/
408
+ account_mailer/
409
+ welcome.tsx
410
+ _components/
411
+ email_layout.tsx
412
+ ```
413
+
414
+ Ignored files can still be imported by email components.
251
415
 
252
416
  ### Layouts
253
417
 
254
- Action Mailer layouts aren't applied to `react:` emails. React Email treats layouts like any other component, so share structure with normal React composition instead:
418
+ Action Mailer layouts are not applied to `react:` emails. In React Email, layouts are normal React components:
255
419
 
256
420
  ```tsx
257
421
  // app/javascript/emails/_components/email_layout.tsx
258
-
259
422
  import { Body, Container, Html } from "@react-email/components"
260
423
  import type { ReactNode } from "react"
261
424
 
@@ -276,7 +439,6 @@ export function EmailLayout({ children }: EmailLayoutProps) {
276
439
 
277
440
  ```tsx
278
441
  // app/javascript/emails/account_mailer/welcome.tsx
279
-
280
442
  import { Text } from "@react-email/components"
281
443
  import { EmailLayout } from "../_components/email_layout"
282
444
 
@@ -289,21 +451,28 @@ export default function Welcome() {
289
451
  }
290
452
  ```
291
453
 
292
- See [Component Names](#component-names) for how shared `_` files are handled.
454
+ ### Editor Documents
293
455
 
294
- ### Editor
295
-
296
- If you're also using [@react-email/editor](https://react.email/docs/editor) to let users compose emails inside your app, `ReactEmailRails.compose` can render those stored documents on the server.
456
+ If your app uses [@react-email/editor](https://react.email/docs/editor) to let users compose emails visually, `ReactEmailRails.compose` can render stored editor documents on the server.
297
457
 
298
458
  See [Editor rendering](docs/editor.md) for setup and usage.
299
459
 
300
460
  ## Configuration
301
461
 
302
- Configuration is handled primarily on the Rails side, though there are some Vite options to be aware of.
462
+ Configuration lives in two places:
463
+
464
+ - Rails configuration controls mailer behavior, prop handling, rendering mode, timeouts, and error hooks.
465
+ - Vite configuration controls email component discovery and the renderer bundle.
303
466
 
304
467
  ### Rails Configuration
305
468
 
306
- If the defaults don't fit, override them in `config/initializers/react_email_rails.rb`:
469
+ Override Rails-side defaults in `config/initializers/react_email_rails.rb`:
470
+
471
+ ```ruby
472
+ ReactEmailRails.configure do |config|
473
+ # config.render_mode = :persistent
474
+ end
475
+ ```
307
476
 
308
477
  | Option | Default |
309
478
  |--------|---------|
@@ -314,15 +483,16 @@ If the defaults don't fit, override them in `config/initializers/react_email_rai
314
483
  | `render_timeout` | `10` seconds |
315
484
  | `render_process_max_requests` | `1_000` |
316
485
  | `on_render_error` | `nil` |
486
+ | `deep_merge_shared_props` | `false` |
317
487
 
318
- #### Prop Transformation
488
+ ### Prop Transformation
319
489
 
320
- Set `transform_props` to another supported value if you prefer a different prop key style:
490
+ Set `transform_props` to choose how Ruby prop keys are exposed to React:
321
491
 
322
492
  | Value | Example |
323
493
  |-------|---------|
324
494
  | `:camel` | `AccountName` |
325
- | `:lower_camel` (default) | `accountName` |
495
+ | `:lower_camel` | `accountName` |
326
496
  | `:dash` | `account-name` |
327
497
  | `:snake` | `account_name` |
328
498
  | `:none` | preserves serialized keys |
@@ -333,15 +503,15 @@ ReactEmailRails.configure do |config|
333
503
  end
334
504
  ```
335
505
 
336
- `transform_props` only controls prop key names. Props are always serialized with `as_json`.
506
+ Only prop keys are transformed. Values are always serialized with `as_json`.
337
507
 
338
- #### Render Modes
508
+ ### Render Modes
339
509
 
340
- `:subprocess` starts a fresh Node process for each render. It's simple, isolated, and always uses the latest bundle, but pays Node startup and bundle load each time.
510
+ The default `:subprocess` mode starts a fresh Node process for each render. It is simple, isolated, and always uses the latest bundle, but it pays Node startup and bundle load time for each email.
341
511
 
342
- `:persistent` reuses one long-lived Node process per worker. It's faster for render-heavy workers, but uses more memory and can serve a stale component until recycled. The default `:subprocess` mode is usually enough. Switch when Node startup shows up in traces or batch jobs render many emails from the same bundle.
512
+ `:persistent` mode keeps one long-lived Node child process per Ruby process. It is faster for render-heavy workers, but uses more memory and can serve a stale component until the child is recycled.
343
513
 
344
- Enable persistent mode for render-heavy worker processes:
514
+ Switch to persistent mode when Node startup shows up in traces or when a worker renders many emails from the same bundle:
345
515
 
346
516
  ```ruby
347
517
  ReactEmailRails.configure do |config|
@@ -349,21 +519,22 @@ ReactEmailRails.configure do |config|
349
519
  end
350
520
  ```
351
521
 
352
- Persistent mode keeps one Node child per process:
522
+ Persistent mode details:
353
523
 
354
- - Renders are newline-delimited JSON and processed one at a time. Scale throughput with more worker processes.
355
- - It's fork-safe: under clustered Puma or forking job runners, each worker spawns its own child.
356
- - The child is recycled after `render_process_max_requests` renders to bound memory growth. Set it to `nil` to disable recycling.
524
+ - Renders are newline-delimited JSON and processed one at a time.
525
+ - Throughput scales with more worker processes.
526
+ - It is fork-safe; clustered Puma and forking job runners spawn one Node child per worker.
527
+ - The child recycles after `render_process_max_requests` renders. Set that option to `nil` to disable recycling.
357
528
 
358
- #### Render Options
529
+ ### Render Options
359
530
 
360
- `render_options` is passed to [@react-email/render](https://react.email/docs/utilities/render). Use `html` and `text` keys for each output. Option keys are camelized before they cross into JavaScript.
531
+ `render_options` is passed to [@react-email/render](https://react.email/docs/utilities/render). Use `html` and `text` keys to configure each output. Option keys are camelized before they cross into JavaScript.
361
532
 
362
533
  ```ruby
363
534
  ReactEmailRails.configure do |config|
364
535
  config.render_options = {
365
536
  html: {
366
- pretty: Rails.env.development?
537
+ pretty: Rails.env.development?,
367
538
  },
368
539
  text: {
369
540
  html_to_text_options: {
@@ -374,9 +545,11 @@ ReactEmailRails.configure do |config|
374
545
  end
375
546
  ```
376
547
 
377
- #### Error Reporting
548
+ `render_options` can also be a callable. When rendering from a mailer, it is evaluated in the mailer instance so it can use helpers or per-email state.
378
549
 
379
- Use `on_render_error` to report failures before the exception is re-raised. The callback receives the error plus `kind:` and `component:`:
550
+ ### Error Reporting
551
+
552
+ Use `on_render_error` to report failures before the exception is re-raised:
380
553
 
381
554
  ```ruby
382
555
  ReactEmailRails.configure do |config|
@@ -386,33 +559,50 @@ ReactEmailRails.configure do |config|
386
559
  end
387
560
  ```
388
561
 
389
- #### Instrumentation
562
+ The callback receives the error plus render context such as `kind:` and `component:`.
563
+
564
+ ### Instrumentation
390
565
 
391
- Every render emits `render.react-email-rails` through [ActiveSupport::Notifications](https://guides.rubyonrails.org/active_support_instrumentation.html). The payload includes `kind`, `component`, and successful HTML size in `html_bytes`:
566
+ Every render emits `render.react-email-rails` through [ActiveSupport::Notifications](https://guides.rubyonrails.org/active_support_instrumentation.html). Email payloads include `kind`, `component`, and successful HTML size in `html_bytes`.
392
567
 
393
568
  ```ruby
394
569
  ActiveSupport::Notifications.subscribe("render.react-email-rails") do |event|
395
- Rails.logger.info("[react-email-rails] Rendered #{event.payload[:component]} (Duration: #{event.duration.round}ms | Size: #{event.payload[:html_bytes]} bytes)")
570
+ Rails.logger.info(
571
+ "[react-email-rails] Rendered #{event.payload[:component]} " \
572
+ "(#{event.duration.round}ms, #{event.payload[:html_bytes]} bytes)",
573
+ )
396
574
  end
397
575
  ```
398
576
 
399
577
  ### Vite Configuration
400
578
 
401
- Most apps only need the `reactEmailRails()` plugin from [Quick Start](#quick-start). The options below change component discovery, bundle dependency handling, and isolated renderer transforms.
579
+ Most apps only need the plugin from [Installation](#installation):
580
+
581
+ ```ts
582
+ import { defineConfig } from "vite"
583
+ import { reactEmailRails } from "react-email-rails"
584
+
585
+ export default defineConfig({
586
+ plugins: [reactEmailRails()],
587
+ })
588
+ ```
589
+
590
+ The isolated renderer loads `reactEmailRails()`, JSX support, and component-facing Vite config such as `resolve`, `define`, `css`, `json`, `assetsInclude`, `esbuild`, and `oxc`. It does not load your other app plugins unless you explicitly add them with `vite.plugins`.
402
591
 
403
- In development and production, the isolated renderer loads `reactEmailRails()`, JSX support, and component-facing Vite config such as `resolve`, `define`, `css`, `json`, `assetsInclude`, `esbuild`, and `oxc`. It doesn't load your other app plugins. Server, preview, dependency optimization, and build output settings stay owned by react-email-rails.
592
+ Server, preview, dependency optimization, and build output settings stay owned by react-email-rails so Rails can always find the renderer bundle.
404
593
 
405
- #### Plugin Options
594
+ ### Vite Plugin Options
406
595
 
407
596
  | Option | Default | Description |
408
597
  |--------|---------|-------------|
409
598
  | `emails.path` | `"app/javascript/emails"` | Directory containing email components |
410
599
  | `emails.extension` | `[".tsx", ".jsx"]` | Component extension, or an array of extensions |
411
600
  | `emails.ignore` | `["**/_*", "**/_*/**"]` | Glob patterns ignored under `emails.path` |
601
+ | `documents` | `false` | Optional `@react-email/editor` document renderer discovery; see [Editor rendering](docs/editor.md) |
412
602
  | `standalone` | `true` | Inline production renderer bundle dependencies |
413
603
  | `vite` | `{}` | Extra email-only Vite config for compilation and resolution |
414
604
 
415
- Use a custom directory:
605
+ Use a custom email directory:
416
606
 
417
607
  ```ts
418
608
  reactEmailRails({
@@ -420,7 +610,7 @@ reactEmailRails({
420
610
  })
421
611
  ```
422
612
 
423
- Use multiple extensions:
613
+ Use custom extensions:
424
614
 
425
615
  ```ts
426
616
  reactEmailRails({
@@ -430,11 +620,11 @@ reactEmailRails({
430
620
  })
431
621
  ```
432
622
 
433
- Component names come from the Vite directory layout (see [Component Names](#component-names)). To map mailer actions to a different layout, override `component_path_resolver` on the Ruby side rather than renaming in the plugin, so both halves stay in sync.
623
+ Component names come from the Vite directory layout. To map mailer actions to a different layout, override `component_path_resolver` on the Ruby side so both halves stay in sync.
434
624
 
435
- #### Advanced: Email-Only Vite Plugins
625
+ ### Email-Only Vite Plugins
436
626
 
437
- Most apps don't need extra email plugins. If email components need a transform that isn't part of Vite's default pipeline, add that transform to the isolated renderer:
627
+ Most apps do not need extra email plugins. If email components need a transform that is not part of Vite's default pipeline, add it to the isolated renderer:
438
628
 
439
629
  ```ts
440
630
  import mdx from "@mdx-js/rollup"
@@ -452,13 +642,13 @@ export default defineConfig({
452
642
  })
453
643
  ```
454
644
 
455
- These `vite` options are used by `react-email-rails-dev` and `react-email-rails-build`. Only `assetsInclude`, `css`, `define`, `esbuild`, `json`, `oxc`, `plugins`, and `resolve` are accepted. Output settings such as `build.outDir` and `build.rollupOptions` are ignored so Ruby can always find the bundle.
645
+ Only `assetsInclude`, `css`, `define`, `esbuild`, `json`, `oxc`, `plugins`, and `resolve` are accepted inside `vite`. Output settings such as `build.outDir` and `build.rollupOptions` are ignored.
456
646
 
457
- #### Standalone Builds
647
+ ### Standalone Builds
458
648
 
459
- By default the production renderer bundle inlines React, `@react-email/render`, and other Node dependencies. This works well for Rails deploys that build assets in one stage and run without `node_modules` in the final image. Development previews keep dependencies external, even when `standalone` is enabled.
649
+ By default, the production renderer bundle inlines React, `@react-email/render`, and other Node dependencies. This works well for Rails deploys that build assets in one stage and run without `node_modules` in the final image.
460
650
 
461
- Set `standalone: false` when your runtime already ships `node_modules` and you prefer a smaller SSR-style bundle:
651
+ Set `standalone: false` when your runtime ships `node_modules` and you prefer a smaller SSR-style bundle:
462
652
 
463
653
  ```ts
464
654
  reactEmailRails({
@@ -466,45 +656,43 @@ reactEmailRails({
466
656
  })
467
657
  ```
468
658
 
469
- Externalized bundles are smaller and may build faster, but the renderer needs the externalized packages available at runtime.
659
+ Externalized bundles can be smaller and may build faster, but the externalized packages must be available at runtime.
470
660
 
471
661
  ## Deployment
472
662
 
473
- For production deploys, run the normal Rails asset task:
663
+ Production deploys should run the normal Rails asset task:
474
664
 
475
665
  ```sh
476
666
  bin/rails assets:precompile
477
667
  ```
478
668
 
479
- The `react_email_rails:build` task is hooked into `assets:precompile` automatically. It loads `reactEmailRails()` options from your Vite config, then writes `tmp/react-email-rails/emails.js` with the email component registry. If [Editor document rendering](docs/editor.md) is enabled, the bundle also includes document renderers.
669
+ react-email-rails hooks `react_email_rails:build` into `assets:precompile`. The build task loads `reactEmailRails()` options from your Vite config and writes `tmp/react-email-rails/emails.js` with the email component registry. If [Editor rendering](docs/editor.md) is enabled, the bundle also includes document renderers.
480
670
 
481
- You can run it directly when needed:
671
+ You can run the renderer build directly:
482
672
 
483
673
  ```sh
484
674
  bin/rails react_email_rails:build
485
675
  ```
486
676
 
487
- Production rendering runs that bundle with Node. Set `SKIP_REACT_EMAIL_RAILS_BUILD=1` to skip the automatic asset hook. Directly running `bin/rails react_email_rails:build` always attempts the build.
677
+ Production rendering requires that bundle. If it is missing, rendering raises `ReactEmailRails::RenderError` and Action Mailer does not send the email.
488
678
 
489
- The npm package, Vite, React, and `@react-email/render` must be available when Rails runs `assets:precompile`. If you enable [Editor document rendering](docs/editor.md), its peer dependencies must be available too.
679
+ Set `SKIP_REACT_EMAIL_RAILS_BUILD=1` to skip the automatic asset hook. Directly running `bin/rails react_email_rails:build` always attempts the build.
490
680
 
491
- The bundle is required, not an optimization. If it's missing, renders raise `ReactEmailRails::RenderError`. Action Mailer deliveries aren't sent.
681
+ The npm package, Vite, React, and `@react-email/render` must be available when Rails runs `assets:precompile`. If [Editor rendering](docs/editor.md) is enabled, its peer dependencies must be available too.
492
682
 
493
- The Ruby gem and npm package must stay on the same version. The renderer includes a small protocol/version handshake, so mismatched installs fail with an actionable `ReactEmailRails::RenderError` instead of silently returning malformed output.
683
+ The Ruby gem and npm package must stay on the same version. A protocol/version handshake catches mismatched installs and raises an actionable `ReactEmailRails::RenderError`.
494
684
 
495
- The build command preserves `emails.path`, `emails.extension`, `emails.ignore`, `standalone`, and email-only `vite` options.
685
+ ### Verify the Renderer
496
686
 
497
- ### Renderer Verification
498
-
499
- To confirm the renderer is ready before relying on it, run:
687
+ Run this before relying on the production renderer:
500
688
 
501
689
  ```sh
502
690
  bin/rails react_email_rails:verify
503
691
  ```
504
692
 
505
- It checks that the render command runs and that the npm package version matches the gem, then exits non-zero with an actionable message on failure. Wire it into CI or release steps to catch missing bundles or version drift before the first render.
693
+ It checks that the render command runs and that the npm package version matches the gem. It exits non-zero with an actionable message on failure, so it is safe to wire into CI or release steps.
506
694
 
507
- For programmatic checks (for example, a health endpoint), `ReactEmailRails.healthy?` returns a boolean. If you specifically want a check at boot, call it from your own initializer and scope it to the processes that send mail so others don't pay the cost:
695
+ For programmatic checks, `ReactEmailRails.healthy?` returns a boolean:
508
696
 
509
697
  ```ruby
510
698
  Rails.application.config.after_initialize do
@@ -514,13 +702,31 @@ Rails.application.config.after_initialize do
514
702
  end
515
703
  ```
516
704
 
705
+ If you check at boot, scope it to processes that send mail so the rest of the app does not pay the cost.
706
+
517
707
  ## Development
518
708
 
519
709
  See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, checks, formatting, and release verification.
520
710
 
711
+ The short version:
712
+
713
+ ```sh
714
+ bundle install
715
+ cd vite && pnpm install
716
+ ```
717
+
718
+ Run the core checks before opening a pull request:
719
+
720
+ ```sh
721
+ ruby scripts/check_version_sync.rb
722
+ bin/test
723
+ bin/lint
724
+ cd vite && pnpm run build
725
+ ```
726
+
521
727
  ## Contributing
522
728
 
523
- Bug reports and pull requests are welcome on GitHub. See [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
729
+ Bug reports and pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request so the local checks and release expectations are clear.
524
730
 
525
731
  ## Security
526
732
 
@@ -528,4 +734,4 @@ Please report vulnerabilities privately. See [SECURITY.md](SECURITY.md) for deta
528
734
 
529
735
  ## License
530
736
 
531
- The gem and npm package are available as open source under the terms of the [MIT License](LICENSE.md).
737
+ The Ruby gem and npm package are available as open source under the terms of the [MIT License](LICENSE.md).
@@ -164,7 +164,6 @@ class ReactEmailRails::Generators::EmailGenerator < Rails::Generators::NamedBase
164
164
  )
165
165
  end
166
166
 
167
- # First capture-group match across an ordered list of patterns, or nil.
168
167
  def first_capture(source, *patterns)
169
168
  patterns.each do |pattern|
170
169
  match = source[pattern, 1]
@@ -1,6 +1,9 @@
1
1
  module ReactEmailRails::ActionMailer
2
2
  extend(ActiveSupport::Concern)
3
3
 
4
+ # `react_email_share` kwargs reserved for `before_action` filtering, not prop data.
5
+ SHARED_FILTER_OPTIONS = [:if, :unless, :only, :except].freeze
6
+
4
7
  prepended do
5
8
  class_attribute(:react_email_use_instance_props, default: false)
6
9
  end
@@ -9,6 +12,19 @@ module ReactEmailRails::ActionMailer
9
12
  def use_react_instance_props
10
13
  self.react_email_use_instance_props = true
11
14
  end
15
+
16
+ def react_email_share(hash = nil, **props, &block)
17
+ options = props.slice(*SHARED_FILTER_OPTIONS)
18
+ data = hash || props.except(*SHARED_FILTER_OPTIONS)
19
+
20
+ before_action(**options) do
21
+ react_email_append_shared(data, block)
22
+ end
23
+ end
24
+ end
25
+
26
+ def react_email_share(hash = nil, **props, &block)
27
+ react_email_append_shared(hash || props, block)
12
28
  end
13
29
 
14
30
  def mail(headers = {}, &block)
@@ -17,15 +33,41 @@ module ReactEmailRails::ActionMailer
17
33
  headers = headers.dup
18
34
  react = headers.delete(:react)
19
35
  props = headers.delete(:props) if headers.key?(:props)
36
+ deep_merge = headers.delete(:deep_merge) if headers.key?(:deep_merge)
20
37
 
21
38
  component, resolved_props = ReactEmailRails::PropsResolver.new(self).resolve(react, props)
22
- render_options = ReactEmailRails.configuration.resolve_render_options(self)
23
- rendered = ReactEmailRails.render(component:, props: resolved_props, render_options:)
39
+ resolved_props = ReactEmailRails::SharedProps.new(self).merge_into(
40
+ resolved_props,
41
+ deep_merge: react_email_deep_merge?(deep_merge),
42
+ )
24
43
 
25
44
  super(headers) do |format|
45
+ rendered = react_email_render(component, resolved_props)
46
+
26
47
  format.html { rendered.html }
27
48
  format.text { rendered.text } if rendered.text.present?
49
+
28
50
  yield(format) if block
29
51
  end
30
52
  end
53
+
54
+ private
55
+
56
+ def react_email_render(component, props)
57
+ props = ReactEmailRails::MailerContext.new(self).merge_into(props)
58
+ render_options = ReactEmailRails.configuration.resolve_render_options(self)
59
+ ReactEmailRails.render(component:, props:, render_options:)
60
+ end
61
+
62
+ def react_email_append_shared(data, block)
63
+ store = (@_react_email_shared ||= [])
64
+ store << data.dup.freeze if data.present?
65
+ store << block if block
66
+ end
67
+
68
+ def react_email_deep_merge?(override)
69
+ return ReactEmailRails.configuration.deep_merge_shared_props if override.nil?
70
+
71
+ override
72
+ end
31
73
  end
@@ -34,6 +34,7 @@ class ReactEmailRails::Configuration
34
34
  :render_options,
35
35
  :transform_props,
36
36
  :on_render_error,
37
+ :deep_merge_shared_props,
37
38
  )
38
39
 
39
40
  attr_reader(
@@ -52,6 +53,7 @@ class ReactEmailRails::Configuration
52
53
  config.render_process_max_requests = DEFAULT_RENDER_PROCESS_MAX_REQUESTS
53
54
  config.transform_props = :lower_camel
54
55
  config.on_render_error = nil
56
+ config.deep_merge_shared_props = false
55
57
  end
56
58
  end
57
59
  end
@@ -0,0 +1,40 @@
1
+ class ReactEmailRails::MailerContext
2
+ MESSAGE_FIELDS = [:subject, :to, :cc, :bcc, :from, :reply_to].freeze
3
+
4
+ def initialize(mailer)
5
+ @mailer = mailer
6
+ end
7
+
8
+ def merge_into(props)
9
+ serialized_props = props.as_json
10
+ return props unless serialized_props.is_a?(Hash)
11
+
12
+ to_h.merge(serialized_props)
13
+ end
14
+
15
+ def to_h
16
+ {
17
+ "mailer" => mailer_context,
18
+ "message" => message_context,
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader(:mailer)
25
+
26
+ def mailer_context
27
+ {
28
+ "mailer_name" => mailer.class.mailer_name,
29
+ "action_name" => mailer.action_name,
30
+ }
31
+ end
32
+
33
+ def message_context
34
+ message = mailer.message
35
+
36
+ MESSAGE_FIELDS.each_with_object({}) do |field, context|
37
+ context[field.to_s] = message.public_send(field)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ # Collects props registered with `react_email_share` and merges them beneath the
2
+ # per-mail props, which win on conflict. Mirrors inertia-rails shared data.
3
+ class ReactEmailRails::SharedProps
4
+ IVAR = :@_react_email_shared
5
+
6
+ def initialize(mailer)
7
+ @mailer = mailer
8
+ end
9
+
10
+ # Returns `props` untouched when nothing is shared, so non-Hash inputs (e.g.
11
+ # serializers) still flow straight through to serialization.
12
+ def merge_into(props, deep_merge:)
13
+ shared = to_h
14
+ return props if shared.empty?
15
+
16
+ base = shared.as_json
17
+ incoming = props.as_json
18
+ deep_merge ? base.deep_merge(incoming) : base.merge(incoming)
19
+ end
20
+
21
+ def to_h
22
+ entries.each_with_object({}) do |entry, props|
23
+ props.merge!(resolve(entry))
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader(:mailer)
30
+
31
+ def entries
32
+ mailer.instance_variable_get(IVAR) || []
33
+ end
34
+
35
+ # An entry is either a static Hash or a block evaluated at render time. Callable
36
+ # values inside a Hash are evaluated too, so `unread_count: -> { ... }` works.
37
+ def resolve(entry)
38
+ hash = entry.respond_to?(:call) ? mailer.instance_exec(&entry) : entry
39
+
40
+ (hash || {}).transform_values do |value|
41
+ value.respond_to?(:call) ? mailer.instance_exec(&value) : value
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module ReactEmailRails
2
- VERSION = "0.4.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -5,6 +5,7 @@ require("active_support/concern")
5
5
  require("active_support/notifications")
6
6
  require("active_support/core_ext/object/blank")
7
7
  require("active_support/core_ext/object/json")
8
+ require("active_support/core_ext/hash/deep_merge")
8
9
  require("active_support/inflector")
9
10
  require("rails/railtie")
10
11
 
@@ -24,6 +25,8 @@ require_relative("react_email_rails/render_modes/persistent/command_runner")
24
25
  require_relative("react_email_rails/configuration")
25
26
  require_relative("react_email_rails/tasks")
26
27
  require_relative("react_email_rails/props_resolver")
28
+ require_relative("react_email_rails/shared_props")
29
+ require_relative("react_email_rails/mailer_context")
27
30
  require_relative("react_email_rails/railtie")
28
31
 
29
32
  module ReactEmailRails
@@ -56,8 +59,7 @@ module ReactEmailRails
56
59
  perform(payload:, label: type, kind: "document", type:)
57
60
  end
58
61
 
59
- # Parse semantic HTML or Markdown into an editor document Hash using the renderer's
60
- # extensions. Pass exactly one of `html:` or `markdown:`.
62
+ # Returns an editor document Hash, built via the renderer's extensions. Pass exactly one of `html:`/`markdown:`.
61
63
  def parse(type:, html: nil, markdown: nil, context: {})
62
64
  payload = {
63
65
  kind: "parse",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react-email-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Supertape
@@ -153,6 +153,7 @@ files:
153
153
  - lib/react_email_rails.rb
154
154
  - lib/react_email_rails/action_mailer.rb
155
155
  - lib/react_email_rails/configuration.rb
156
+ - lib/react_email_rails/mailer_context.rb
156
157
  - lib/react_email_rails/props_resolver.rb
157
158
  - lib/react_email_rails/railtie.rb
158
159
  - lib/react_email_rails/render_error.rb
@@ -164,6 +165,7 @@ files:
164
165
  - lib/react_email_rails/render_modes/subprocess/command_runner.rb
165
166
  - lib/react_email_rails/render_protocol.rb
166
167
  - lib/react_email_rails/rendered_email.rb
168
+ - lib/react_email_rails/shared_props.rb
167
169
  - lib/react_email_rails/tasks.rb
168
170
  - lib/react_email_rails/version.rb
169
171
  - lib/tasks/react_email_rails/build.rake