@unlayer/react-elements 0.1.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.
- package/README.md +432 -0
- package/dist/index.cjs +1621 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +852 -0
- package/dist/index.d.ts +852 -0
- package/dist/index.js +1588 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# @unlayer/react-elements
|
|
2
|
+
|
|
3
|
+
React components for building emails, pages, and documents with Unlayer Elements. Full SSR support — works with `renderToHtml`, `renderToString`, Next.js, Remix, and any server-side framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @unlayer/react-elements
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @unlayer/react-elements
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Email, Row, Column, ColumnLayouts, Heading, Paragraph, Button } from '@unlayer/react-elements';
|
|
17
|
+
|
|
18
|
+
function WelcomeEmail() {
|
|
19
|
+
return (
|
|
20
|
+
<Email backgroundColor="#f0f0f0" contentWidth="600px">
|
|
21
|
+
<Row layout={ColumnLayouts.TwoEqual} backgroundColor="#ffffff" padding="20px">
|
|
22
|
+
<Column>
|
|
23
|
+
<Heading
|
|
24
|
+
text="Hello World"
|
|
25
|
+
fontSize="24px"
|
|
26
|
+
fontFamily={{ label: "Arial", value: "arial,helvetica,sans-serif" }}
|
|
27
|
+
/>
|
|
28
|
+
</Column>
|
|
29
|
+
<Column>
|
|
30
|
+
<Paragraph text="Welcome to our newsletter!" fontSize="14px" />
|
|
31
|
+
</Column>
|
|
32
|
+
</Row>
|
|
33
|
+
<Row layout={ColumnLayouts.OneColumn} padding="10px">
|
|
34
|
+
<Column>
|
|
35
|
+
<Button
|
|
36
|
+
text="Click Me"
|
|
37
|
+
href="https://example.com"
|
|
38
|
+
backgroundColor="#0879A1"
|
|
39
|
+
color="#ffffff"
|
|
40
|
+
/>
|
|
41
|
+
</Column>
|
|
42
|
+
</Row>
|
|
43
|
+
</Email>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Critical Rules
|
|
49
|
+
|
|
50
|
+
These props have non-obvious shapes that **must** be followed exactly:
|
|
51
|
+
|
|
52
|
+
- **fontFamily**: Must be `{ label: string, value: string }`, NOT a plain string.
|
|
53
|
+
```tsx
|
|
54
|
+
fontFamily={{ label: "Arial", value: "arial, sans-serif" }}
|
|
55
|
+
```
|
|
56
|
+
- **fontWeight**: Must be a number (`400`, `700`), NOT a string (`"400"`).
|
|
57
|
+
- **Wrapper component**: Use `<Email>`, `<Page>`, or `<Document>` as root — they set the rendering mode automatically.
|
|
58
|
+
- **href**: Can be a plain string URL (auto-wrapped) or `{ name: "web", values: { href, target } }`.
|
|
59
|
+
- **Image src**: Can be a plain string URL (auto-wrapped) or `{ url, width?, autoWidth?, maxWidth? }`.
|
|
60
|
+
- **children**: Text components accept children as shorthand for `text` prop. `<Paragraph>Hello</Paragraph>` = `<Paragraph text="Hello" />`.
|
|
61
|
+
|
|
62
|
+
## Structure: Email/Page/Document > Row > Column > Items
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
<Email> ← root wrapper for email-safe HTML (tables)
|
|
66
|
+
<Page> ← root wrapper for responsive web (div + flexbox)
|
|
67
|
+
<Document> ← root wrapper for print/PDF
|
|
68
|
+
|
|
69
|
+
<Row> ← layout container, use layout={ColumnLayouts.X} or cells={[1,1]}
|
|
70
|
+
<Column> ← must match layout column count
|
|
71
|
+
<Button /> ← item components go inside columns
|
|
72
|
+
<Paragraph />
|
|
73
|
+
</Column>
|
|
74
|
+
</Row>
|
|
75
|
+
</Email>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Components
|
|
79
|
+
|
|
80
|
+
### Root Wrappers
|
|
81
|
+
|
|
82
|
+
| Component | Description |
|
|
83
|
+
|-----------|-------------|
|
|
84
|
+
| `<Email>` | Root wrapper for email-safe HTML (tables for Outlook, Gmail, Yahoo). |
|
|
85
|
+
| `<Page>` | Root wrapper for responsive web display (div + flexbox). |
|
|
86
|
+
| `<Document>` | Root wrapper for print-optimized / PDF rendering. |
|
|
87
|
+
|
|
88
|
+
### Layout
|
|
89
|
+
|
|
90
|
+
| Component | Description |
|
|
91
|
+
|-----------|-------------|
|
|
92
|
+
| `<Row>` | Layout container. Accepts `layout={ColumnLayouts.X}` or `cells={[1, 1]}`. |
|
|
93
|
+
| `<Column>` | Column inside a Row. Number of Columns must match the layout. |
|
|
94
|
+
|
|
95
|
+
### Content
|
|
96
|
+
|
|
97
|
+
| Component | Description |
|
|
98
|
+
|-----------|-------------|
|
|
99
|
+
| `<Button>` | CTA button with hover states, links, and full styling |
|
|
100
|
+
| `<Paragraph>` | Rich text with `text` (plain) or `html` (formatted) props |
|
|
101
|
+
| `<Heading>` | Heading (h1-h4) with `level` shorthand |
|
|
102
|
+
| `<Image>` | Responsive image with `src` / `alt` shorthands |
|
|
103
|
+
| `<Divider>` | Horizontal rule / separator |
|
|
104
|
+
| `<Social>` | Social media icons with `icons` shorthand array |
|
|
105
|
+
| `<Menu>` | Navigation menu with `items` shorthand array |
|
|
106
|
+
| `<Table>` | Data table with `headers` / `data` shorthands |
|
|
107
|
+
| `<Video>` | Video embed with `videoUrl` shorthand |
|
|
108
|
+
| `<Html>` | Custom HTML passthrough |
|
|
109
|
+
|
|
110
|
+
## Component Reference
|
|
111
|
+
|
|
112
|
+
### Email
|
|
113
|
+
Root wrapper for email-safe HTML. Same props as Body (without `mode`).
|
|
114
|
+
- `backgroundColor?: string` — `"#F7F8F9"`
|
|
115
|
+
- `contentWidth?: string` — `"500px"`
|
|
116
|
+
- `contentAlign?: string` — `"center"`
|
|
117
|
+
- `fontFamily?: { label: string, value: string }` — `{ label: "Arial", value: "arial,helvetica,sans-serif" }`
|
|
118
|
+
- `textColor?: string` — `"#000000"`
|
|
119
|
+
- `linkStyle?: { linkColor, linkHoverColor, linkUnderline, linkHoverUnderline }`
|
|
120
|
+
- `previewText?: string` — preview text shown in email client inboxes
|
|
121
|
+
|
|
122
|
+
### Page
|
|
123
|
+
Root wrapper for responsive web display. Same props as Email.
|
|
124
|
+
|
|
125
|
+
### Document
|
|
126
|
+
Root wrapper for print/PDF rendering. Same props as Email.
|
|
127
|
+
|
|
128
|
+
### Row
|
|
129
|
+
Layout container. Must be child of Email/Page/Document/Body.
|
|
130
|
+
- `layout?: ColumnLayout` — use `ColumnLayouts.X`
|
|
131
|
+
- `cells?: number[]` — alternative to layout
|
|
132
|
+
- `backgroundColor?: string`
|
|
133
|
+
- `padding?: string` — `"0px"`
|
|
134
|
+
- `noStackMobile?: boolean` — `false`
|
|
135
|
+
|
|
136
|
+
### Column
|
|
137
|
+
Must be child of Row. Count must match layout.
|
|
138
|
+
- `padding?: string` — `"10px"`
|
|
139
|
+
- `backgroundColor?: string`
|
|
140
|
+
- `borderRadius?: string`
|
|
141
|
+
|
|
142
|
+
### Button
|
|
143
|
+
- `text?: string` — `"Button"` (or use children)
|
|
144
|
+
- `href?: string | Href` — plain string auto-wrapped
|
|
145
|
+
- `backgroundColor?: string` — `"#0879A1"`
|
|
146
|
+
- `color?: string` — `"#FFFFFF"`
|
|
147
|
+
- `hoverBackgroundColor?: string`
|
|
148
|
+
- `hoverColor?: string`
|
|
149
|
+
- `fontSize?: string` — `"14px"`
|
|
150
|
+
- `fontWeight?: number` — `400`
|
|
151
|
+
- `fontFamily?: { label: string, value: string }`
|
|
152
|
+
- `padding?: string` — `"10px 20px"`
|
|
153
|
+
- `borderRadius?: string` — `"4px"`
|
|
154
|
+
- `textAlign?: "left" | "center" | "right"` — `"center"`
|
|
155
|
+
- `containerPadding?: string`
|
|
156
|
+
|
|
157
|
+
### Paragraph
|
|
158
|
+
- `text?: string` — plain text (auto-converted to Lexical JSON internally)
|
|
159
|
+
- `html?: string` — **rich HTML string** with inline formatting: `<b>`, `<i>`, `<u>`, `<s>`, `<a>`, `<code>`
|
|
160
|
+
- `fontSize?: string` — `"14px"`
|
|
161
|
+
- `color?: string` — `"#000000"`
|
|
162
|
+
- `textAlign?: "left" | "center" | "right"` — `"left"`
|
|
163
|
+
- `lineHeight?: string` — `"140%"`
|
|
164
|
+
- `fontFamily?: { label: string, value: string }`
|
|
165
|
+
- `containerPadding?: string`
|
|
166
|
+
|
|
167
|
+
Priority: `html` > `text` > children. Use `html` for formatted text, `text` for plain text.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<Paragraph text="Plain text paragraph" fontSize="14px" />
|
|
171
|
+
<Paragraph html="Hello <b>bold</b> and <a href='#'>link</a>" fontSize="14px" />
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Heading
|
|
175
|
+
- `text?: string` — `"Heading"` (or use children)
|
|
176
|
+
- `headingType?: "h1" | "h2" | "h3" | "h4"` — `"h1"`
|
|
177
|
+
- `level?: "h1" | "h2" | "h3" | "h4"` — shorthand for headingType
|
|
178
|
+
- `fontSize?: string` — `"22px"`
|
|
179
|
+
- `fontWeight?: number` — `400`
|
|
180
|
+
- `fontFamily?: { label: string, value: string }`
|
|
181
|
+
- `color?: string` — `"#000000"`
|
|
182
|
+
- `textAlign?: "left" | "center" | "right"` — `"left"`
|
|
183
|
+
- `lineHeight?: string` — `"110%"`
|
|
184
|
+
- `containerPadding?: string`
|
|
185
|
+
|
|
186
|
+
### Divider
|
|
187
|
+
- `borderTopWidth?: string` — `"1px"`
|
|
188
|
+
- `borderTopColor?: string` — `"#BBBBBB"`
|
|
189
|
+
- `borderTopStyle?: string` — `"solid"`
|
|
190
|
+
- `textAlign?: "left" | "center" | "right"` — `"center"`
|
|
191
|
+
- `containerPadding?: string`
|
|
192
|
+
|
|
193
|
+
### Image
|
|
194
|
+
- `src?: string | { url, width?, autoWidth?, maxWidth? }` — string URLs auto-wrapped
|
|
195
|
+
- `altText?: string` — alt text for accessibility
|
|
196
|
+
- `textAlign?: "left" | "center" | "right"` — `"center"`
|
|
197
|
+
- `action?: { name: "web", values: { href, target } }`
|
|
198
|
+
- `containerPadding?: string`
|
|
199
|
+
|
|
200
|
+
### Video
|
|
201
|
+
- `videoUrl?: string` — YouTube/Vimeo URL, auto-parsed
|
|
202
|
+
- `video?: { type: "youtube" | "vimeo", videoId, thumbnail }` — manual control
|
|
203
|
+
- `containerPadding?: string`
|
|
204
|
+
|
|
205
|
+
### Html
|
|
206
|
+
- `html?: string` — `"<p>Custom HTML content</p>"`
|
|
207
|
+
- `containerPadding?: string`
|
|
208
|
+
|
|
209
|
+
### Table
|
|
210
|
+
- `headers?: string[]` — shorthand for column headers
|
|
211
|
+
- `data?: string[][]` — shorthand for row data
|
|
212
|
+
- `columns?: number` — `3`
|
|
213
|
+
- `rows?: number` — `3`
|
|
214
|
+
- `enableHeader?: boolean` — `true`
|
|
215
|
+
- `containerPadding?: string`
|
|
216
|
+
|
|
217
|
+
### Social
|
|
218
|
+
- `icons?: { name: string, url: string }[]` — shorthand
|
|
219
|
+
- `iconType?: "circle" | "rounded" | "squared"` — `"circle"`
|
|
220
|
+
- `iconSize?: number` — `32`
|
|
221
|
+
- `spacing?: number` — `10`
|
|
222
|
+
- `align?: "left" | "center" | "right"` — `"center"`
|
|
223
|
+
- `containerPadding?: string`
|
|
224
|
+
|
|
225
|
+
### Menu
|
|
226
|
+
- `items?: { text: string, href: string, target?: string }[]` — shorthand
|
|
227
|
+
- `layout?: "horizontal" | "vertical"` — `"horizontal"`
|
|
228
|
+
- `separator?: string` — `"|"`
|
|
229
|
+
- `align?: "left" | "center" | "right"` — `"center"`
|
|
230
|
+
- `containerPadding?: string`
|
|
231
|
+
|
|
232
|
+
## Column Layouts
|
|
233
|
+
|
|
234
|
+
Pre-built layouts for common column configurations:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { Row, Column, ColumnLayouts } from '@unlayer/react-elements';
|
|
238
|
+
|
|
239
|
+
<Row layout={ColumnLayouts.OneColumn}> {/* [1] → 100% */}
|
|
240
|
+
<Row layout={ColumnLayouts.TwoEqual}> {/* [1,1] → 50% + 50% */}
|
|
241
|
+
<Row layout={ColumnLayouts.TwoWideNarrow}> {/* [2,1] → 67% + 33% */}
|
|
242
|
+
<Row layout={ColumnLayouts.TwoNarrowWide}> {/* [1,2] → 33% + 67% */}
|
|
243
|
+
<Row layout={ColumnLayouts.ThreeEqual}> {/* [1,1,1] → 33% each */}
|
|
244
|
+
<Row layout={ColumnLayouts.ThreeNarrowWideNarrow}> {/* [1,2,1] → 25% + 50% + 25% */}
|
|
245
|
+
<Row layout={ColumnLayouts.FourEqual}> {/* [1,1,1,1] → 25% each */}
|
|
246
|
+
<Row layout={ColumnLayouts.FiveEqual}> {/* [1,1,1,1,1] → 20% each */}
|
|
247
|
+
<Row cells={[3, 1]}> {/* Custom ratio */}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Number of `<Column>` children must match the layout.
|
|
251
|
+
|
|
252
|
+
## Rendering Modes
|
|
253
|
+
|
|
254
|
+
Use the semantic wrapper component that matches your target:
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
<Email>...</Email> // Email-client safe (tables for Outlook, Gmail, Yahoo)
|
|
258
|
+
<Page>...</Page> // Responsive web (div + flexbox)
|
|
259
|
+
<Document>...</Document> // Print/PDF optimized
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Each wrapper threads the correct mode to all children automatically.
|
|
263
|
+
|
|
264
|
+
## renderToHtml
|
|
265
|
+
|
|
266
|
+
Render any element tree to a clean HTML string — no React hydration markers, perfect for email sending and PDF generation:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { renderToHtml, Email, Row, Column, ColumnLayouts, Paragraph, Button } from '@unlayer/react-elements';
|
|
270
|
+
|
|
271
|
+
const html = renderToHtml(
|
|
272
|
+
<Email backgroundColor="#f4f4f4">
|
|
273
|
+
<Row layout={ColumnLayouts.OneColumn}>
|
|
274
|
+
<Column>
|
|
275
|
+
<Paragraph text="Hello World" fontSize="14px" />
|
|
276
|
+
<Button
|
|
277
|
+
text="Click me"
|
|
278
|
+
backgroundColor="#3b82f6"
|
|
279
|
+
color="#ffffff"
|
|
280
|
+
/>
|
|
281
|
+
</Column>
|
|
282
|
+
</Row>
|
|
283
|
+
</Email>
|
|
284
|
+
);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## UnlayerProvider
|
|
288
|
+
|
|
289
|
+
Configure global settings like CDN base URL, merge tags, text direction, and rendering mode:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { UnlayerProvider, Email, Row, Column, Social, Menu } from '@unlayer/react-elements';
|
|
293
|
+
|
|
294
|
+
function App() {
|
|
295
|
+
return (
|
|
296
|
+
<UnlayerProvider config={{
|
|
297
|
+
cdnBaseUrl: "https://my-cdn.example.com",
|
|
298
|
+
mergeTagState: { firstName: "Jane", company: "Acme" },
|
|
299
|
+
textDirection: "ltr"
|
|
300
|
+
}}>
|
|
301
|
+
<Email>
|
|
302
|
+
<Row layout={ColumnLayouts.OneColumn}>
|
|
303
|
+
<Column>
|
|
304
|
+
<Social icons={[{ name: "Facebook", url: "https://facebook.com/acme" }]} />
|
|
305
|
+
<Menu items={[{ text: "Home", href: "/" }, { text: "About", href: "/about" }]} />
|
|
306
|
+
</Column>
|
|
307
|
+
</Row>
|
|
308
|
+
</Email>
|
|
309
|
+
</UnlayerProvider>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Important:** The root wrapper (`Email`/`Page`/`Document`) bridges the provider context to child components. Components inside `UnlayerProvider` but without a root wrapper won't receive the config.
|
|
315
|
+
|
|
316
|
+
## Types
|
|
317
|
+
|
|
318
|
+
All types are exported and sourced from `@unlayer/types`:
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import type {
|
|
322
|
+
ButtonValues, SocialValues, TableValues,
|
|
323
|
+
Href, Icons, TextAlign, LinkStyle,
|
|
324
|
+
SocialIcon, MenuItem,
|
|
325
|
+
ButtonProps, MenuProps, ImageProps,
|
|
326
|
+
} from '@unlayer/react-elements';
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Common Font Stacks
|
|
330
|
+
|
|
331
|
+
fontFamily must always be an object. Here are ready-to-use stacks:
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
const sansFont = { label: "Sans Serif", value: "system-ui, -apple-system, BlinkMacSystemFont, sans-serif" };
|
|
335
|
+
const serifFont = { label: "Georgia", value: "Georgia, 'Times New Roman', Times, serif" };
|
|
336
|
+
const monoFont = { label: "Monospace", value: "'SF Mono', 'Fira Code', 'Roboto Mono', monospace" };
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Common Design Patterns
|
|
340
|
+
|
|
341
|
+
### Header with Logo
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
<Row layout={ColumnLayouts.OneColumn} backgroundColor="#ffffff" padding="24px 40px">
|
|
345
|
+
<Column>
|
|
346
|
+
<Image src="https://example.com/logo.png" altText="Logo" textAlign="left" />
|
|
347
|
+
</Column>
|
|
348
|
+
</Row>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Accent Bar
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
<Row layout={ColumnLayouts.OneColumn} backgroundColor="#4f46e5" padding="0">
|
|
355
|
+
<Column>
|
|
356
|
+
<Divider borderTopWidth="3px" borderTopColor="#4f46e5" borderTopStyle="solid" containerPadding="0" />
|
|
357
|
+
</Column>
|
|
358
|
+
</Row>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Feature Grid (2×2)
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
<Row layout={ColumnLayouts.TwoEqual} backgroundColor="#ffffff" padding="24px 40px">
|
|
365
|
+
<Column>
|
|
366
|
+
<Heading text="Feature 1" headingType="h3" fontSize="16px" fontWeight={600} color="#1a1a1a" />
|
|
367
|
+
<Paragraph text="Description of the feature." fontSize="13px" color="#71717a" />
|
|
368
|
+
</Column>
|
|
369
|
+
<Column>
|
|
370
|
+
<Heading text="Feature 2" headingType="h3" fontSize="16px" fontWeight={600} color="#1a1a1a" />
|
|
371
|
+
<Paragraph text="Description of the feature." fontSize="13px" color="#71717a" />
|
|
372
|
+
</Column>
|
|
373
|
+
</Row>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Metric Cards (3-column)
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
<Row layout={ColumnLayouts.ThreeEqual} backgroundColor="#ffffff" padding="0 40px">
|
|
380
|
+
<Column>
|
|
381
|
+
<Heading text="1.2M" headingType="h2" fontSize="28px" fontWeight={700} color="#0f172a" textAlign="center" />
|
|
382
|
+
<Paragraph text="API Calls" fontSize="12px" color="#94a3b8" textAlign="center" />
|
|
383
|
+
</Column>
|
|
384
|
+
{/* Repeat for each metric */}
|
|
385
|
+
</Row>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Product Card (image + details)
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
<Row layout={ColumnLayouts.TwoNarrowWide} backgroundColor="#ffffff" padding="20px 40px">
|
|
392
|
+
<Column>
|
|
393
|
+
<Image src="https://example.com/product.jpg" altText="Product" />
|
|
394
|
+
</Column>
|
|
395
|
+
<Column>
|
|
396
|
+
<Heading text="Product Name" headingType="h3" fontSize="16px" fontWeight={600} color="#1a1a1a" />
|
|
397
|
+
<Paragraph text="Matte White · Medium" fontSize="13px" color="#a1a1aa" />
|
|
398
|
+
<Heading text="$89.00" headingType="h3" fontSize="16px" fontWeight={700} color="#1a1a1a" />
|
|
399
|
+
</Column>
|
|
400
|
+
</Row>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Footer
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
<Row layout={ColumnLayouts.OneColumn} padding="20px 40px 40px 40px">
|
|
407
|
+
<Column>
|
|
408
|
+
<Paragraph text="Company Name · City, State" fontSize="12px" color="#a1a1aa" textAlign="center" />
|
|
409
|
+
</Column>
|
|
410
|
+
</Row>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Common Mistakes
|
|
414
|
+
|
|
415
|
+
1. **fontFamily as string** — `fontFamily="Arial"` → Must be `fontFamily={{ label: "Arial", value: "arial, sans-serif" }}`
|
|
416
|
+
2. **fontWeight as string** — `fontWeight="700"` → Must be `fontWeight={700}`
|
|
417
|
+
3. **Column count mismatch** — `TwoEqual` layout requires exactly 2 `<Column>` children
|
|
418
|
+
4. **Missing Column** — Items must be inside `<Column>`, never directly in `<Row>`
|
|
419
|
+
5. **Missing Row** — Columns must be inside `<Row>`, never directly in `<Email>`/`<Page>`/`<Document>`
|
|
420
|
+
6. **containerPadding vs padding** — `padding` is on Row/Column containers; `containerPadding` is per-item internal spacing
|
|
421
|
+
|
|
422
|
+
## Development
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
pnpm build # Build the package
|
|
426
|
+
pnpm test # Run tests
|
|
427
|
+
pnpm storybook # Launch Storybook
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## License
|
|
431
|
+
|
|
432
|
+
MIT
|