diff --git a/.changeset/rich-days-love.md b/.changeset/rich-days-love.md new file mode 100644 index 0000000000..8a9d9f6e19 --- /dev/null +++ b/.changeset/rich-days-love.md @@ -0,0 +1,8 @@ +--- +"@react-email/columns": minor +"@react-email/stack": minor +"docs": patch +"@react-email/components": patch +--- + +- Add new layout primitives: Columns and Stack components with gap support and docs updates. diff --git a/apps/docs/components/columns.mdx b/apps/docs/components/columns.mdx new file mode 100644 index 0000000000..d77b92b39b --- /dev/null +++ b/apps/docs/components/columns.mdx @@ -0,0 +1,116 @@ +--- +title: "Columns" +sidebarTitle: "Columns" +description: "Place content side by side in two or more columns. Equal or custom widths, with optional spacing—no need to hand-wire Row and Column." +"og:image": "https://react.email/static/covers/section.png" +icon: "columns" +--- + +import Support from '/snippets/support.mdx' + +## Install + +Install component from your command line. + + + +```sh npm +npm install @react-email/components -E + +# or get the individual package + +npm install @react-email/columns -E +``` + +```sh yarn +yarn add @react-email/components -E + +# or get the individual package + +yarn add @react-email/columns -E +``` + +```sh pnpm +pnpm add @react-email/components -E + +# or get the individual package + +pnpm add @react-email/columns -E +``` + + + +## Getting started + +Use Columns to place content side by side in two or more columns. Add it inside a **Section**; set `gap` for space between columns and optionally `columnWidths` for custom widths. + +```jsx +import { Section, Columns, Text } from "@react-email/components"; + +const Email = () => ( +
+ + Left column + Right column + +
+); +``` + +## Compared to Row + Column + +The same layout can be built with **Row** and **Column**—manual widths and padding for gap. Columns is a thin wrapper that does it with one component and optional `gap` and `columnWidths` props. + +**With Row + Column (primitives):** + +```jsx +import { Section, Row, Column, Text } from "@react-email/components"; + +const Email = () => ( +
+ + + Left column + + + Right column + + +
+); +``` + +**With Columns (same result, less code):** + +```jsx +import { Section, Columns, Text } from "@react-email/components"; + +const Email = () => ( +
+ + Left column + Right column + +
+); +``` + +## Props + + + Number of columns for equal-width layout. Defaults to the number of children. + + + + Space between columns. Implemented with spacer columns and calc() widths so total width stays 100%. A number is treated as pixels; a string is used as-is. + + + + Explicit width per column (e.g. `["60%", "40%"]`). When provided, overrides equal splitting. + + + + Inline styles applied to the underlying row. + + + diff --git a/apps/docs/components/stack.mdx b/apps/docs/components/stack.mdx new file mode 100644 index 0000000000..dc3d122806 --- /dev/null +++ b/apps/docs/components/stack.mdx @@ -0,0 +1,117 @@ +--- +title: "Stack" +sidebarTitle: "Stack" +description: "Place blocks one under the other with consistent spacing between them. No need to hand-wire rows or margins." +"og:image": "https://react.email/static/covers/section.png" +icon: "layer-group" +--- + +import Support from '/snippets/support.mdx' + +## Install + +Install component from your command line. + + + +```sh npm +npm install @react-email/components -E + +# or get the individual package + +npm install @react-email/stack -E +``` + +```sh yarn +yarn add @react-email/components -E + +# or get the individual package + +yarn add @react-email/stack -E +``` + +```sh pnpm +pnpm add @react-email/components -E + +# or get the individual package + +pnpm add @react-email/stack -E +``` + + + +## Getting started + +Use Stack to place blocks one under the other with consistent spacing. Add it inside a **Section**; set `gap` for the space between items. + +```jsx +import { Section, Stack, Text } from "@react-email/components"; + +const Email = () => ( +
+ + First block + Second block + Third block + +
+); +``` + +## Compared to Section + Row + +The same layout can be built with **Section**, **Row**, and **Column** — one row per block, each block in a Column. Stack is a thin wrapper around **Row** and **Column** (same primitives as **Columns**) that does it with one component and a single `gap` prop. + +**With Section + Row (primitives):** + +```jsx +import { Section, Row, Column, Text } from "@react-email/components"; + +const Email = () => ( +
+ + + First block + + + + + Second block + + + + + Third block + + +
+); +``` + +**With Stack (same result, less code):** + +```jsx +import { Section, Stack, Text } from "@react-email/components"; + +const Email = () => ( +
+ + First block + Second block + Third block + +
+); +``` + +## Props + + + Spacing between stacked children. A number is treated as pixels (e.g. `16` → `16px`); a string is used as-is (e.g. `"1em"`). + + + + Inline styles applied to each row (Stack renders one Row per child). + + + diff --git a/apps/docs/docs.json b/apps/docs/docs.json index adb6f5849e..4b836804c6 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -70,6 +70,7 @@ "components/code-block", "components/code-inline", "components/column", + "components/columns", "components/row", "components/font", "components/heading", @@ -79,6 +80,7 @@ "components/markdown", "components/preview", "components/section", + "components/stack", "components/tailwind", "components/text" ] diff --git a/packages/columns/CHANGELOG.md b/packages/columns/CHANGELOG.md new file mode 100644 index 0000000000..d2fe948ddd --- /dev/null +++ b/packages/columns/CHANGELOG.md @@ -0,0 +1,7 @@ +# @react-email/columns + +## 0.0.1 + +### Added + +- Initial release: multi-column layout component with configurable widths and gap. diff --git a/packages/columns/license.md b/packages/columns/license.md new file mode 100644 index 0000000000..a2c628c9e0 --- /dev/null +++ b/packages/columns/license.md @@ -0,0 +1,7 @@ +Copyright 2026 Plus Five Five, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/columns/package.json b/packages/columns/package.json new file mode 100644 index 0000000000..b66f2ec069 --- /dev/null +++ b/packages/columns/package.json @@ -0,0 +1,61 @@ +{ + "name": "@react-email/columns", + "version": "0.0.1", + "description": "A multi-column layout component for React Email", + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20.0.0" + }, + "files": [ + "dist/**" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "license": "MIT", + "scripts": { + "build": "tsdown src/index.ts --format esm,cjs --dts --external react", + "build:watch": "tsdown src/index.ts --format esm,cjs --dts --external react --watch", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + }, + "dependencies": { + "@react-email/column": "workspace:0.0.14", + "@react-email/row": "workspace:0.0.13" + }, + "devDependencies": { + "@react-email/render": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "5.8.3" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/resend/react-email.git", + "directory": "packages/columns" + }, + "keywords": [ + "react", + "email", + "layout", + "columns" + ] +} diff --git a/packages/columns/readme.md b/packages/columns/readme.md new file mode 100644 index 0000000000..0788caaec9 --- /dev/null +++ b/packages/columns/readme.md @@ -0,0 +1,60 @@ +![React Email Section cover](https://react.email/static/covers/section.png) + +
@react-email/columns
+
A multi-column layout component with equal or custom widths and optional gap.
+
+
+Website + · +GitHub + +
+ +## Install + +Install component from your command line. + +#### With yarn + +```sh +yarn add @react-email/columns -E +``` + +#### With npm + +```sh +npm install @react-email/columns -E +``` + +## Getting started + +Add the component inside a Section. Include styles where needed. + +```jsx +import { Section } from '@react-email/section'; +import { Columns } from '@react-email/columns'; +import { Text } from '@react-email/text'; + +const Email = () => { + return ( +
+ + Left column + Right column + +
+ ); +}; +``` + +## Support + +This component was tested using the most popular email clients. + +| Gmail logo | Apple Mail | Outlook logo | Yahoo! Mail logo | HEY logo | Superhuman logo | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| Gmail ✔ | Apple Mail ✔ | Outlook ✔ | Yahoo! Mail ✔ | HEY ✔ | Superhuman ✔ | + +## License + +MIT License diff --git a/packages/columns/src/columns.spec.tsx b/packages/columns/src/columns.spec.tsx new file mode 100644 index 0000000000..2d4c755272 --- /dev/null +++ b/packages/columns/src/columns.spec.tsx @@ -0,0 +1,69 @@ +import { render } from '@react-email/render'; +import { Columns } from './index'; + +describe(' component', () => { + it('renders children correctly', async () => { + const html = await render( + + Left + Right + , + ); + expect(html).toContain('Left'); + expect(html).toContain('Right'); + }); + + it('passes style and other props correctly', async () => { + const html = await render( + + One + , + ); + expect(html).toContain('data-testid="cols"'); + expect(html).toContain('background-color:red'); + }); + + it('applies equal widths when columnWidths not provided', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('width:50%'); + }); + + it('applies custom columnWidths', async () => { + const html = await render( + + Main + Side + , + ); + expect(html).toContain('width:60%'); + expect(html).toContain('width:40%'); + }); + + it('applies gap via spacer columns (Outlook-safe, no overflow)', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('width:16px'); + expect(html).toContain('calc((100% - 16px) / 2)'); + }); + + it('renders correctly', async () => { + const actualOutput = await render( + + One + Two + , + ); + expect(actualOutput).toMatchInlineSnapshot( + `"
OneTwo
"`, + ); + }); +}); diff --git a/packages/columns/src/columns.tsx b/packages/columns/src/columns.tsx new file mode 100644 index 0000000000..5ccb57d793 --- /dev/null +++ b/packages/columns/src/columns.tsx @@ -0,0 +1,114 @@ +import { Column } from '@react-email/column'; +import { Row } from '@react-email/row'; +import * as React from 'react'; + +function gapToCss(gap: string | number): string { + if (typeof gap === 'number') return `${gap}px`; + return String(gap); +} + +export type ColumnsProps = Readonly< + Omit, 'children'> & { + /** Number of columns (default: number of children). */ + cols?: number; + /** + * Space between columns (number as px, or string). + * Uses spacer cells + calc() widths so total width stays 100% (avoids overflow in Outlook). + * Note: Outlook desktop does not support calc(); it will still show the gap and approximate column widths. + */ + gap?: string | number; + /** Explicit width per column (e.g. ['50%', '50%'] or ['60%', '40%']). */ + columnWidths?: string[]; + children: React.ReactNode; + } +>; + +export const Columns = React.forwardRef( + ( + { children, cols: colsProp, gap = 0, columnWidths, style, ...props }, + ref, + ) => { + const items = React.Children.toArray(children); + const cols = colsProp ?? items.length; + const hasGap = gap !== 0 && gap !== undefined; + const gapCss = gapToCss(gap); + + const n = items.length; + const totalGapCss = + n <= 1 + ? '0px' + : typeof gap === 'number' + ? `${(n - 1) * gap}px` + : `calc(${n - 1} * (${gapCss}))`; + + const contentWidths: string[] = + columnWidths && columnWidths.length >= n + ? columnWidths.slice(0, n).map((w) => { + const pct = Number.parseFloat(w); + if (!hasGap || !Number.isFinite(pct)) return w; + const fraction = pct / 100; + return `calc((100% - ${totalGapCss}) * ${fraction})`; + }) + : Array.from({ length: n }, () => + hasGap + ? `calc((100% - ${totalGapCss}) / ${cols})` + : `${100 / cols}%`, + ); + + if (!hasGap) { + return ( + + {items.map((child, index) => ( + + {child} + + ))} + + ); + } + + const cells: React.ReactNode[] = []; + items.forEach((child, index) => { + cells.push( + + {child} + , + ); + if (index < items.length - 1) { + cells.push( + + {'\u00A0'} + , + ); + } + }); + + return ( + + {cells} + + ); + }, +); + +Columns.displayName = 'Columns'; diff --git a/packages/columns/src/index.ts b/packages/columns/src/index.ts new file mode 100644 index 0000000000..a7f066b206 --- /dev/null +++ b/packages/columns/src/index.ts @@ -0,0 +1 @@ +export * from './columns'; diff --git a/packages/columns/tsconfig.json b/packages/columns/tsconfig.json new file mode 100644 index 0000000000..cd6c94d6e8 --- /dev/null +++ b/packages/columns/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["."], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/packages/components/package.json b/packages/components/package.json index d8caba9e17..eb722cc1f7 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -45,6 +45,7 @@ "@react-email/code-block": "workspace:0.2.1", "@react-email/code-inline": "workspace:0.0.6", "@react-email/column": "workspace:0.0.14", + "@react-email/columns": "workspace:*", "@react-email/container": "workspace:0.0.16", "@react-email/font": "workspace:0.0.10", "@react-email/head": "workspace:0.0.13", @@ -58,6 +59,7 @@ "@react-email/render": "workspace:2.0.4", "@react-email/row": "workspace:0.0.13", "@react-email/section": "workspace:0.0.17", + "@react-email/stack": "workspace:*", "@react-email/tailwind": "workspace:2.0.4", "@react-email/text": "workspace:0.1.6" }, diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 6c13454481..664f5c5e44 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -3,6 +3,7 @@ export * from '@react-email/button'; export * from '@react-email/code-block'; export * from '@react-email/code-inline'; export * from '@react-email/column'; +export * from '@react-email/columns'; export * from '@react-email/container'; export * from '@react-email/font'; export * from '@react-email/head'; @@ -16,5 +17,6 @@ export * from '@react-email/preview'; export * from '@react-email/render'; export * from '@react-email/row'; export * from '@react-email/section'; +export * from '@react-email/stack'; export * from '@react-email/tailwind'; export * from '@react-email/text'; diff --git a/packages/stack/CHANGELOG.md b/packages/stack/CHANGELOG.md new file mode 100644 index 0000000000..a7fd9fa3d8 --- /dev/null +++ b/packages/stack/CHANGELOG.md @@ -0,0 +1,7 @@ +# @react-email/stack + +## 0.0.1 + +### Added + +- Initial release: vertical stack layout component with configurable `gap` between children. diff --git a/packages/stack/license.md b/packages/stack/license.md new file mode 100644 index 0000000000..a2c628c9e0 --- /dev/null +++ b/packages/stack/license.md @@ -0,0 +1,7 @@ +Copyright 2026 Plus Five Five, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/stack/package.json b/packages/stack/package.json new file mode 100644 index 0000000000..45eadc81c6 --- /dev/null +++ b/packages/stack/package.json @@ -0,0 +1,61 @@ +{ + "name": "@react-email/stack", + "version": "0.0.1", + "description": "A vertical stack layout component for React Email", + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20.0.0" + }, + "files": [ + "dist/**" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "license": "MIT", + "scripts": { + "build": "tsdown src/index.ts --format esm,cjs --dts --external react", + "build:watch": "tsdown src/index.ts --format esm,cjs --dts --external react --watch", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@react-email/column": "workspace:0.0.14", + "@react-email/row": "workspace:0.0.13" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + }, + "devDependencies": { + "@react-email/render": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "5.8.3" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/resend/react-email.git", + "directory": "packages/stack" + }, + "keywords": [ + "react", + "email", + "layout", + "stack" + ] +} diff --git a/packages/stack/readme.md b/packages/stack/readme.md new file mode 100644 index 0000000000..8190cc2e8a --- /dev/null +++ b/packages/stack/readme.md @@ -0,0 +1,61 @@ +![React Email Section cover](https://react.email/static/covers/section.png) + +
@react-email/stack
+
A vertical stack layout component with consistent spacing between children.
+
+
+Website + · +GitHub + +
+ +## Install + +Install component from your command line. + +#### With yarn + +```sh +yarn add @react-email/stack -E +``` + +#### With npm + +```sh +npm install @react-email/stack -E +``` + +## Getting started + +Add the component inside a Section. Include styles where needed. + +```jsx +import { Section } from '@react-email/section'; +import { Stack } from '@react-email/stack'; +import { Text } from '@react-email/text'; + +const Email = () => { + return ( +
+ + First block + Second block + Third block + +
+ ); +}; +``` + +## Support + +This component was tested using the most popular email clients. + +| Gmail logo | Apple Mail | Outlook logo | Yahoo! Mail logo | HEY logo | Superhuman logo | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| Gmail ✔ | Apple Mail ✔ | Outlook ✔ | Yahoo! Mail ✔ | HEY ✔ | Superhuman ✔ | + +## License + +MIT License diff --git a/packages/stack/src/index.ts b/packages/stack/src/index.ts new file mode 100644 index 0000000000..d39a8e6fcb --- /dev/null +++ b/packages/stack/src/index.ts @@ -0,0 +1 @@ +export * from './stack'; diff --git a/packages/stack/src/stack.spec.tsx b/packages/stack/src/stack.spec.tsx new file mode 100644 index 0000000000..1624e9e9ad --- /dev/null +++ b/packages/stack/src/stack.spec.tsx @@ -0,0 +1,61 @@ +import { render } from '@react-email/render'; +import { Stack } from './index'; + +describe(' component', () => { + it('renders children correctly', async () => { + const html = await render( + + First + Second + , + ); + expect(html).toContain('First'); + expect(html).toContain('Second'); + }); + + it('passes style and other props correctly', async () => { + const html = await render( + + Only one + , + ); + expect(html).toContain('data-testid="stack"'); + expect(html).toContain('background-color:red'); + }); + + it('applies gap as paddingBottom on cells (Outlook-safe)', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('padding-bottom:16px'); + }); + + it('accepts gap as string', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('padding-bottom:1em'); + }); + + it('renders correctly', async () => { + const actual = await render( + + One + Two + , + ); + expect(actual).toContain('
One
Two
"`, + ); + }); +}); diff --git a/packages/stack/src/stack.tsx b/packages/stack/src/stack.tsx new file mode 100644 index 0000000000..93e239deba --- /dev/null +++ b/packages/stack/src/stack.tsx @@ -0,0 +1,47 @@ +import { Column } from '@react-email/column'; +import { Row } from '@react-email/row'; +import * as React from 'react'; + +function gapToCss(gap: string | number): string { + if (typeof gap === 'number') return `${gap}px`; + return String(gap); +} + +export type StackProps = Readonly< + Omit, 'children'> & { + /** Spacing between stacked children (number as px, or string e.g. '16px'). */ + gap?: string | number; + children: React.ReactNode; + } +>; + +export const Stack = React.forwardRef( + ({ children, gap = 0, style, ...props }, ref) => { + const items = React.Children.toArray(children); + const gapCss = gapToCss(gap); + + return ( + <> + {items.map((child, index) => ( + + + {child} + + + ))} + + ); + }, +); + +Stack.displayName = 'Stack'; diff --git a/packages/stack/tsconfig.json b/packages/stack/tsconfig.json new file mode 100644 index 0000000000..cd6c94d6e8 --- /dev/null +++ b/packages/stack/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["."], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6793e4a32..48d29e1b8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,7 +119,7 @@ importers: dependencies: mintlify: specifier: 4.2.280 - version: 4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + version: 4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) zod: specifier: 3.24.3 version: 3.24.3 @@ -408,6 +408,28 @@ importers: specifier: 5.8.3 version: 5.8.3 + packages/columns: + dependencies: + '@react-email/column': + specifier: workspace:0.0.14 + version: link:../column + '@react-email/row': + specifier: workspace:0.0.13 + version: link:../row + react: + specifier: ^19.0.0 + version: 19.0.0 + devDependencies: + '@react-email/render': + specifier: workspace:* + version: link:../render + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: 5.8.3 + version: 5.8.3 + packages/components: dependencies: '@react-email/body': @@ -425,6 +447,9 @@ importers: '@react-email/column': specifier: workspace:0.0.14 version: link:../column + '@react-email/columns': + specifier: workspace:* + version: link:../columns '@react-email/container': specifier: workspace:0.0.16 version: link:../container @@ -464,6 +489,9 @@ importers: '@react-email/section': specifier: workspace:0.0.17 version: link:../section + '@react-email/stack': + specifier: workspace:* + version: link:../stack '@react-email/tailwind': specifier: workspace:2.0.4 version: link:../tailwind @@ -1030,6 +1058,28 @@ importers: specifier: 5.8.3 version: 5.8.3 + packages/stack: + dependencies: + '@react-email/column': + specifier: workspace:0.0.14 + version: link:../column + '@react-email/row': + specifier: workspace:0.0.13 + version: link:../row + react: + specifier: ^19.0.0 + version: 19.0.0 + devDependencies: + '@react-email/render': + specifier: workspace:* + version: link:../render + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: 5.8.3 + version: 5.8.3 + packages/tailwind: dependencies: react: @@ -11320,6 +11370,36 @@ snapshots: - acorn - supports-color + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.3 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + '@mdx-js/react@3.1.0(@types/react@19.2.10)(react@19.0.0)': dependencies: '@types/mdx': 2.0.13 @@ -11328,15 +11408,15 @@ snapshots: '@mediapipe/tasks-vision@0.10.17': {} - '@mintlify/cli@4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': + '@mintlify/cli@4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': dependencies: '@inquirer/prompts': 7.9.0(@types/node@25.0.6) '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/link-rot': 3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/link-rot': 3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) '@mintlify/models': 0.0.257 - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) adm-zip: 0.5.16 chalk: 5.2.0 color: 4.2.3 @@ -11487,13 +11567,13 @@ snapshots: - ts-node - typescript - '@mintlify/link-rot@3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + '@mintlify/link-rot@3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) '@mintlify/scraping': 4.0.522(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) fs-extra: 11.1.0 unist-util-visit: 4.1.2 transitivePeerDependencies: @@ -11539,6 +11619,33 @@ snapshots: - supports-color - typescript + '@mintlify/mdx@3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + dependencies: + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@shikijs/transformers': 3.15.0 + '@shikijs/twoslash': 3.15.0(typescript@5.9.3) + arktype: 2.1.27 + hast-util-to-string: 3.0.1 + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm: 3.1.0 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-to-hast: 13.2.0 + next-mdx-remote-client: 1.0.7(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + rehype-katex: 7.0.1 + remark-gfm: 4.0.1 + remark-math: 6.0.0 + remark-smartypants: 3.0.2 + shiki: 3.15.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + transitivePeerDependencies: + - '@types/react' + - acorn + - supports-color + - typescript + '@mintlify/models@0.0.255': dependencies: axios: 1.10.0 @@ -11562,12 +11669,12 @@ snapshots: leven: 4.0.0 yaml: 2.6.1 - '@mintlify/prebuild@1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + '@mintlify/prebuild@1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) '@mintlify/openapi-parser': 0.0.8 '@mintlify/scraping': 4.0.527(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) chalk: 5.3.0 favicons: 7.2.0 front-matter: 4.0.2 @@ -11593,11 +11700,11 @@ snapshots: - typescript - utf-8-validate - '@mintlify/previewing@4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': + '@mintlify/previewing@4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) better-opn: 3.0.2 chalk: 5.2.0 chokidar: 3.5.3 @@ -11742,6 +11849,29 @@ snapshots: - supports-color - typescript + '@mintlify/validation@0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + dependencies: + '@mintlify/mdx': 3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/models': 0.0.257 + arktype: 2.1.27 + js-yaml: 4.1.0 + lcm: 0.0.3 + lodash: 4.17.21 + object-hash: 3.0.0 + openapi-types: 12.1.3 + uuid: 11.1.0 + zod: 3.21.4 + zod-to-json-schema: 3.20.4(zod@3.21.4) + transitivePeerDependencies: + - '@radix-ui/react-popover' + - '@types/react' + - acorn + - debug + - react + - react-dom + - supports-color + - typescript + '@monogrid/gainmap-js@3.1.0(three@0.170.0)': dependencies: promise-worker-transferable: 1.0.4 @@ -17119,9 +17249,9 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - mintlify@4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3): + mintlify@4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3): dependencies: - '@mintlify/cli': 4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/cli': 4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) transitivePeerDependencies: - '@radix-ui/react-popover' - '@types/node' @@ -17199,6 +17329,22 @@ snapshots: - acorn - supports-color + next-mdx-remote-client@1.0.7(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@babel/code-frame': 7.27.1 + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@mdx-js/react': 3.1.0(@types/react@19.2.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + remark-mdx-remove-esm: 1.1.0 + serialize-error: 12.0.0 + vfile: 6.0.3 + vfile-matter: 5.0.0 + transitivePeerDependencies: + - '@types/react' + - acorn + - supports-color + next-safe-action@8.0.11(next@16.1.6(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: next: 16.1.6(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -18048,6 +18194,16 @@ snapshots: transitivePeerDependencies: - acorn + recma-jsx@1.0.0(acorn@8.15.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + recma-parse@1.0.0: dependencies: '@types/estree': 1.0.8