Repository navigation
RFC: extension points for the preview (extra source tabs, a post-render hook) #3842
gazjones00
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Goals
@react-email/ui. The first two places are extra tabs in the source view, and a step that runs on the rendered markup before the preview shows it.Background
We build Shopify notification emails with React Email, in several locales. Like #1374, the templates emit Liquid through a few small primitives (
<Liquid.If>,liquid('order.name'), and so on), and a script exports each template per locale as the Liquid that gets pasted into Shopify. That part works well. The preview is where we've run out of room:{{ 'now' | date: '%Y' }}survives. It runspretty()with an unlimitedprintWidth, because Prettier breaks Liquid string literals at 80 columns. And it prepends a few{% assign %}config lines. So what the HTML tab shows isn't what we paste into Shopify. A "Liquid" tab next to React / HTML / Plain Text is the natural place for it.{{ }}tags, a higher-order component renders the template in isolation with@react-email/render, decodes entities inside the tags, runs LiquidJS over the result with mock data, and passes the string back to React throughuse(). It's a render inside a render, and it exists only because there's no step between "the template rendered" and "the preview shows it".What we tried. Patching
@react-email/ui(6.9.0) to add the tab. The code side is small: the tabs are a fixedmarkupsarray insrc/app/preview/[...slug]/preview.tsx, and their content comes fromrender-email-by-path, which already readsPreviewPropsoff the template. Butemail devstarts Next from the package's prebuilt.nextwithdev: false, so a patch tosrc/does nothing until the UI is rebuilt after every install. The patch also has to be regenerated on every release, because the CLI requires the UI version to match exactly. We decided that's not something we can maintain, which is what prompted this thread.Related threads. #1044 (the 2023 addons RFC) asked for preview controllers and stalled on where a config would live. #1374 was told Shopify support is out of scope, but that a package would make sense, and this would let such a package exist. #3716 covers form controls for preview props.
Proposal
Template-level statics, following the
PreviewPropsconvention. No config file and no plugin loading. The preview server already reads one static off the default export, so this adds two more:PreviewSourcesruns on the server inrender-email-by-path, next to the existingmarkup/plainTextrenders. The results travel in the rendering metadata and show up as extra tabs. Copy and download keep working, usingextensionfor the file name.PreviewTransformchanges only what the preview iframe displays. It doesn't touch whatrender()returns anywhere else, and it doesn't feed the React tab.email buildwould run both at build time withPreviewProps, so built previews get the tabs too.Open question: per template or per project? Liquid hydration is the same for every template, so wrapping each one in a higher-order component, as we do today, is repetitive. Options:
PreviewProps. Projects share behaviour by wrapping templates.emails/preview.config.ts) exportingsourcesandtransform, applied to every template. Less repetition, but it brings back [RFC] Addons #1044's question of how the config is found and loaded.I'd propose (C). (A) is useful on its own, can't affect anyone who doesn't opt in, and leaves (B) as an additive change.
Related but probably separate: variations on built previews. In
email buildoutput, the props panel is read-only ("rendered at build time"), so a deployed preview only ever shows one state of each template. The suggestion on #2339 of multiple namedPreviewPropsper template would fix that, because a finite set of variants can be pre-rendered at build time. That fits #2339 or #3716 better than this thread, so I'll pick it up there unless people would rather keep it together.Things I'd want to agree on before writing code
PreviewSources/PreviewTransformthe right names?PreviewTransformalso feed the HTML tab, or only the iframe? I lean towards the iframe only, so the HTML tab stays an honest view ofrender().email build, for expensive ones?On contributing: happy to implement whichever version comes out of this thread.
All reactions