feat(ui): add props editor to the preview toolbar#3613
Open
melv-n wants to merge 3 commits into
Open
Conversation
🦋 Changeset detectedLatest commit: c22ed98 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
@melv-n is attempting to deploy a commit to the resend Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
There was a problem hiding this comment.
2 issues found across 8 files
Confidence score: 3/5
- In
packages/ui/src/components/toolbar.tsx,activeTabis taken from the URL without checking whether that tab is actually available whenisRawHtmlEmail/isBuildinghide the Props trigger, so the UI can land on an invalid tab state and show missing/incorrect content to users — validate or coerceactiveTabto a visible tab before renderingTabs.Contentand add a quick navigation test for hidden-tab scenarios. - In
packages/ui/src/hooks/use-email-rendering-result.ts, theisFirstOverrideRunguard not resetting on the early unmount path can trigger an extra server action during React Strict Mode double-mount, which adds noise and avoidable backend work in development — reset the guard consistently in cleanup/early-return paths before merging.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/ui/src/components/toolbar.tsx">
<violation number="1" location="packages/ui/src/components/toolbar.tsx:328">
P2: The Props tab trigger is conditionally hidden when `isRawHtmlEmail` or `isBuilding` is true, but `activeTab` comes directly from the URL query param without validating against available tabs, and `Tabs.Content value="props">` unconditionally renders `<PreviewPropsEditor />`. When a user navigates to `?toolbar-panel=props` in a raw HTML preview or static build, the editor renders and mutating the JSON triggers `useEmailRenderingResult` to call `renderEmailByPath` — a server action that is unavailable in static builds, causing a runtime failure. The content should be guarded the same way as the trigger, or `activeTab` should be validated against the supported tabs for the current mode.</violation>
</file>
<file name="packages/ui/src/hooks/use-email-rendering-result.ts">
<violation number="1" location="packages/ui/src/hooks/use-email-rendering-result.ts:26">
P3: The `isFirstOverrideRun` guard in the effect doesn't reset on unmount in its early-return path, which causes an unnecessary server action call in React Strict Mode's double-mount behavior. When navigating to a new email or during HMR in development, the ref persists across mounts, so the component re-enters the effect with `isFirstOverrideRun` already `false` and issues a redundant `renderEmailByPath(emailPath, false, undefined)` call that returns the same data already in initial state.
Returning a cleanup that resets the ref from the early-return path would prevent this.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…uble-mounted effects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the preview props editing that was originally floated in #371 (that issue was closed once
PreviewPropsdefaults landed, so this PR doesn't close anything; it builds the UI-editor half that was discussed there): a Props tab in the preview toolbar (next to Linter/Compatibility/Spam) that lets you live-edit the props a template is rendered with, to try out long values, different alphabets, edge cases, and so on, without touching thePreviewPropsin the template file.What it does
PreviewPropsby default).renderEmailByPathserver action (debounced 400ms). Invalid JSON shows the parse error inline and leaves the preview untouched.PreviewProps..htmltemplates (no props) and in static builds (isBuilding), where the server action does not exist.Implementation notes
renderEmailByPathgains an optionalpreviewPropsOverrideparameter used instead ofEmail.PreviewProps, and the render cache now keys on path + override so default and overridden renders don't clobber each other (invalidatingCacheclears every entry for the path). The returned metadata includespreviewProps, the JSON-safe props the render used, which is what seeds the editor.PreviewProviderand flows throughuseEmailRenderingResult, which re-renders on override changes and passes the current override to the hot-reload re-render (via a ref, so the socket listener isn't re-registered).previewPropsfalls back to{}for those templates and everything else keeps working.How to test
pnpm --filter=react-email deva project (or the demo app), open a template previewUnit coverage added in
render-email-by-path.spec.tsfor the override parameter, the exposedpreviewProps, and cache isolation between default and overridden renders. Full@react-email/uisuite passes (99 tests).Screenshot
Related to #371.