Skip to content

feat(examples): add interactive Next.js App Router example - #53

Open
Cypher-Aura-19 wants to merge 3 commits into
unlayer:mainfrom
Cypher-Aura-19:feat/nextjs-app-router-example
Open

feat(examples): add interactive Next.js App Router example#53
Cypher-Aura-19 wants to merge 3 commits into
unlayer:mainfrom
Cypher-Aura-19:feat/nextjs-app-router-example

Conversation

@Cypher-Aura-19

Copy link
Copy Markdown

Summary

Closes #21.

Adds a focused Next.js 15 App Router example demonstrating server-side rendering of an Unlayer Elements email template using renderToHtml() and renderToPlainText().

Changes

  • Adds a realistic welcome email template built with @unlayer/react-elements.
  • Renders HTML and plain-text output exclusively on the server.
  • Adds an interactive preview with HTML, source and plain-text tabs.
  • Adds desktop and mobile viewport controls.
  • Adds dependency-free clipboard actions.
  • Provides /email-preview/raw for accessing the rendered HTML directly.
  • Includes complete setup and local package-development instructions.
  • Links the example from the root README.

Workspace compatibility

The example is isolated from the root workspace to prevent React 19 dependencies from affecting the repository’s React 18 packages. It consumes the published package like a normal external Next.js application.

The example README also explains how to test local package changes using a packed tarball.

Verification

  • pnpm install in the example: passed
  • pnpm build in the example: passed
  • pnpm dev: verified
  • /: returns 200
  • /email-preview: returns 200
  • /email-preview/raw: returns 200 with text/html; charset=utf-8
  • Root pnpm build: passed
  • Root tests: 538 tests passed

@ivoIturrieta ivoIturrieta left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this example. The production build and all routes work correctly, but I found a few issues to address before merging:

  1. The example pins next@15.5.3, which has a critical React Flight RCE and several high-severity vulnerabilities. pnpm audit --prod currently reports 33 vulnerabilities. Please upgrade to at least next@15.5.21, regenerate the lockfile, and rerun the audit.

  2. Running next build inside the example still causes Next.js to select the monorepo root because it detects both lockfiles. The build emits a workspace-root warning, and the output trace references the repository-root package.json. Please set outputFileTracingRoot to the example directory in next.config.ts.

  3. The root .gitignore now ignores every __snapshots__/ directory in the monorepo. This seems unrelated to the example and could silently hide committed test snapshots. Please remove it or scope it narrowly to the example.

- Upgrade Next.js from 15.5.3 to 15.5.22 (fixes 33 audit vulnerabilities)
- Add pnpm overrides for postcss>=8.5.18 and sharp>=0.35.0 to clear remaining transitive vulnerabilities
- Set outputFileTracingRoot to __dirname in next.config.ts to prevent workspace-root tracing warning
- .gitignore: no __snapshots__/ rule exists in the PR diff, left unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Cypher-Aura-19

Copy link
Copy Markdown
Author

Thanks for the review. I’ve pushed ec69618 addressing the requested changes:

  • Upgraded the example from next@15.5.3 to next@15.5.22.
  • Regenerated the example lockfile.
  • Added outputFileTracingRoot: __dirname in the example next.config.ts.
  • Confirmed the example build no longer emits the workspace-root warning.
  • Ran pnpm audit --prod in the example and it now reports 0 vulnerabilities.

Verification:

  • pnpm install in the example passed
  • pnpm audit --prod in the example passed with 0 vulnerabilities
  • pnpm build in the example passed
  • root pnpm build passed
  • root pnpm test passed: 538 tests

For the .gitignore note: I checked the current PR diff against main, and this branch does not add a __snapshots__/ rule to the root or package .gitignore files. That may have applied to an earlier branch state, so I left .gitignore unchanged.

@ivoIturrieta

Copy link
Copy Markdown
Collaborator

Thanks — the previous review points are addressed: Next.js is upgraded, the output-tracing warning is gone, and no snapshot ignore rule was added.

I found one remaining blocker:

  1. The example is incompatible with the repository-pinned pnpm@9.7.0. From a clean checkout,
    running pnpm install inside the example fails with:

    ERR_PNPM_LOCKFILE_CONFIG_MISMATCH

    Forcing installation with --no-frozen-lockfile removes the postcss and sharp overrides
    from the lockfile and resolves vulnerable versions. pnpm audit --prod then reports five
    vulnerabilities, including three high-severity findings.

    The committed lockfile installs, builds, and audits successfully with pnpm 11. Please either pin
    a compatible pnpm version in the example.

Additional corrections:

  1. WelcomeEmail says its props may come from a DB, webhook, or CMS, but plan is interpolated
    into the raw html prop and dashboardUrl is passed through without validation. The package
    preserves injected markup and javascript: URLs, and the preview iframe is unsandboxed. The
    current constants are safe, but the example should escape dynamic HTML, validate URLs, and sandbox
    the iframe.

  2. The homepage says “No client JavaScript, no use client,” but PreviewDashboard.tsx is a
    client component. Please clarify that email construction and rendering are server-only while the
    interactive dashboard uses client JavaScript.

Move the postcss/sharp overrides into the package.json pnpm field and drop
the pnpm-11-only pnpm-workspace.yaml overrides + allowBuilds placeholder, so
a clean checkout installs with the repository-pinned pnpm@9.7.0 instead of
failing with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. The regenerated lockfile
audits clean (0 vulnerabilities).

Escape the dynamic plan value and validate dashboardUrl as http(s) in
WelcomeEmail, and sandbox the preview iframe, so untrusted props cannot inject
markup or javascript: URLs.

Clarify on the homepage that email construction and rendering are server-only
while the interactive dashboard uses client JavaScript.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Cypher-Aura-19

Copy link
Copy Markdown
Author

Thanks for the thorough review. Pushed 5f84f6a addressing all four points:

1. pnpm@9.7.0 compatibility (blocker). Root cause: the lockfile was generated with pnpm 11, which reads overrides from pnpm-workspace.yaml; the repo-pinned pnpm 9.7.0 reads them from the package.json pnpm field, so it saw no overrides and the frozen check failed with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. Fix:

  • Moved the postcss/sharp overrides into the package.json pnpm field.
  • Added "packageManager": "pnpm@9.7.0" to the example.
  • Removed the pnpm-11-only overrides + allowBuilds placeholder from pnpm-workspace.yaml.
  • Regenerated the lockfile with pnpm 9.7.0.

Verified from a clean checkout with pnpm 9.7.0: pnpm install --frozen-lockfile passes, pnpm audit --prod reports 0 vulnerabilities, and pnpm build succeeds.

2. Untrusted props in WelcomeEmail. Added src/utils/safe.ts with escapeHtml() and requireSafeUrl(). plan is now escaped before it is interpolated into the raw html prop, and dashboardUrl is validated as an http(s) URL (rejecting javascript: and malformed input).

3. Unsandboxed preview iframe. Added sandbox="" to the preview iframe so the rendered email runs with no privileges. Confirmed the email still renders.

4. Homepage client/server claim. Reworded to clarify that email construction and rendering are server-only, while the interactive preview dashboard is the part that uses client JavaScript.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Example: Next.js App Router

2 participants