Skip to content

Email templates with logo #852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
Open

Email templates with logo #852

wants to merge 9 commits into from

Conversation

fomalhautb
Copy link
Contributor

@fomalhautb fomalhautb commented Aug 19, 2025

image

Important

Add logo support to email templates by passing logo URLs and project display names to email rendering functions and themes.

  • Email Rendering:
    • Add themeProps with logoUrl, fullLogoUrl, and projectDisplayName to renderEmailWithTemplate() in render-email/route.tsx and send-email/route.tsx.
    • Update renderEmailWithTemplate() in email-rendering.tsx to accept themeProps and pass them to the EmailTheme component.
  • Email Themes:
    • Modify LightEmailTheme and DarkEmailTheme in emails.ts to display logos using Img component.
    • Add conditional rendering for fullLogoUrl and logoUrl in EmailTheme components.
  • Type Definitions:
    • Update ThemeProps in code-editor.tsx to include logoUrl, fullLogoUrl, and projectDisplayName.

This description was created by Ellipsis for 7bce171. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Emails now include project branding: full or compact logo plus project name in Light and Dark themes.
    • Email previews and sent messages receive branding and unsubscribe data so templates render project-specific styling.
  • Style

    • Improved contrast/readability of the unsubscribe link in the Dark theme.

Copy link

vercel bot commented Aug 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Aug 23, 2025 0:02am
stack-dashboard Ready Ready Preview Comment Aug 23, 2025 0:02am
stack-demo Ready Ready Preview Comment Aug 23, 2025 0:02am
stack-docs Ready Ready Preview Comment Aug 23, 2025 0:02am

Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Email rendering now accepts a unified themeProps object carrying branding (projectDisplayName, logoUrl, fullLogoUrl, unsubscribeLink). Backend routes pass themeProps into renderEmailWithTemplate, the renderer forwards it into EmailTheme, and Light/Dark themes render logos and project name when present; dashboard typings updated.

Changes

Cohort / File(s) Summary
Backend API routes
apps/backend/src/app/api/latest/emails/render-email/route.tsx, apps/backend/src/app/api/latest/emails/send-email/route.tsx
POST handlers updated to pass a themeProps object to renderEmailWithTemplate (now including unsubscribeLink plus branding fields); imports reordered/cleaned.
Email rendering library
apps/backend/src/lib/email-rendering.tsx
renderEmailWithTemplate signature changed to accept themeProps?: { unsubscribeLink?, projectDisplayName?, logoUrl?, fullLogoUrl? }; preview props merged and spread into EmailTheme; removed unused import.
Dashboard editor typings
apps/dashboard/src/components/vibe-coding/code-editor.tsx
Inline augmentation for module @stackframe/emails extends ThemeProps with optional projectDisplayName, logoUrl, fullLogoUrl to update Monaco/editor typings.
Shared email themes
packages/stack-shared/src/helpers/emails.ts
Light and Dark EmailTheme signatures updated to accept branding props; templates render fullLogo or logo + projectDisplayName when provided and adjust dark theme unsubscribe styling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant API as /emails/send-email (POST)
  participant Renderer as renderEmailWithTemplate
  participant Theme as EmailTheme (Light/Dark)
  participant Template as EmailTemplate

  Client->>API: Send email request
  API->>Renderer: renderEmailWithTemplate(template, theme, { variables, themeProps:{ unsubscribeLink, projectDisplayName, logoUrl, fullLogoUrl }, previewMode })
  Renderer->>Theme: Render with {...themeProps}
  Theme->>Theme: Conditionally render header (fullLogo → logo + name → none)
  Theme->>Template: Wrap template content with themed layout
  Renderer-->>API: { html, text, subject?, notificationCategory? }
  API-->>Client: Return/send result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • init emails docs #848 — Modifies the same backend send-email route and similarly touches renderEmailWithTemplate usage and import organization.

Poem

I nibble code with whiskered cheer,
Logos hop in, tidy and clear,
Unsubscribe tucked where it belongs,
Project name hums its little song,
Hop hop — themed emails now appear.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f0bc56a and f0cfc76.

📒 Files selected for processing (1)
  • apps/backend/src/app/api/latest/emails/send-email/route.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/app/api/latest/emails/send-email/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: all-good
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: Security Check
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch email-templates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

claude bot commented Aug 19, 2025

Claude finished @fomalhautb's task —— View job


Pull Request Review

Reviewing the "Email templates with logo" changes for code quality, security, performance, and potential issues.

Todo List

  • Read and analyze all 5 changed files
  • Review code quality and best practices
  • Check for potential bugs and security issues
  • Assess performance considerations
  • Evaluate test coverage implications
  • Provide comprehensive feedback with specific references

Review Summary

This PR adds logo support to email templates by extending the theme props to include logoUrl and fullLogoUrl properties. The changes are well-structured and follow existing patterns in the codebase.

Positive Aspects

  1. Consistent Implementation: Logo support is properly threaded through all relevant layers:

    • API endpoints (render-email/route.tsx, send-email/route.tsx)
    • Email rendering logic (email-rendering.tsx)
    • TypeScript definitions (code-editor.tsx, emails.ts)
  2. Good Fallback Logic: The theme templates handle logo display gracefully:

    • fullLogoUrl takes precedence over logoUrl
    • Falls back to project display name when no logo is available
    • Proper conditional rendering prevents empty logo elements
  3. Type Safety: TypeScript definitions are properly updated in both the @stackframe/emails module declaration and ThemeProps interface.

⚠️ Issues & Recommendations

Code Quality Issues

  1. Duplicate Import Statement in packages/stack-shared/src/helpers/emails.ts:45-46:

    import { Html, Head, Tailwind, Body, Container, Link } from '@react-email/components';
    import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';

    The first import is redundant and should be removed.

  2. Missing Alt Text Validation: While alt attributes are provided, there's no validation that logoUrl and fullLogoUrl are valid URLs or accessible images. Consider adding basic URL validation.

Security Considerations

  1. URL Validation Missing: The logo URLs are passed directly to <Img> components without validation. While this follows existing patterns in the codebase, consider:
    • Adding URL format validation
    • Sanitizing URLs to prevent potential XSS (though React provides some protection)
    • Consider if there should be allowlisting of domains for logo URLs

Performance Considerations

  1. Image Loading: No optimization for image loading is implemented:

    • No lazy loading attributes
    • No width/height hints for layout stability
    • Consider adding image optimization hints
  2. Double Rendering: In send-email/route.tsx:107-115, when using template_id, the email is rendered twice - once to get the notification category and again for the final send. This is inefficient but appears to be existing behavior, not introduced by this PR.

Minor Issues

  1. Styling Inconsistency: The logo styling differs between light and dark themes:
    • Light theme: className="h-16" for full logo, className="h-8" for regular logo
    • Dark theme: Same classes but different text color handling
    • This is consistent but consider documenting the sizing choices

Test Coverage Recommendations

  1. Missing Test Scenarios:
    • Logo rendering with valid logoUrl
    • Logo rendering with valid fullLogoUrl
    • Fallback behavior when logos are undefined/null
    • Email rendering with both logo types simultaneously
    • Logo URL validation edge cases

Code Locations

  • Logo props added to theme props: apps/backend/src/app/api/latest/emails/render-email/route.tsx:58-62
  • Email sending with logo props: apps/backend/src/app/api/latest/emails/send-email/route.tsx:158-163
  • Theme template updates: packages/stack-shared/src/helpers/emails.ts:49-63, 87-101
  • TypeScript definitions: apps/dashboard/src/components/vibe-coding/code-editor.tsx:97-98

Recommended Actions

  1. Fix duplicate import in emails.ts:45-46
  2. Add basic URL validation for logo URLs
  3. Add tests for logo rendering scenarios
  4. Consider image optimization attributes for better performance
  5. Document logo sizing decisions in code comments

Overall, this is a solid implementation that follows established patterns. The main concern is the duplicate import that should be fixed before merging.


Copy link

claude bot commented Aug 19, 2025

Claude finished @fomalhautb's task —— View job


Code Review Complete ✅

I've thoroughly reviewed PR #852 that adds logo support to email templates. Here's my comprehensive feedback:

Todo Checklist

  • Read and analyze all 5 changed files
  • Review code quality and best practices
  • Check for potential bugs and issues
  • Assess performance considerations
  • Identify security concerns
  • Evaluate test coverage needs
  • Provide comprehensive feedback

🔍 Review Summary

The implementation correctly adds logo support to email templates with proper type safety and consistent patterns. The changes are well-structured and follow existing codebase conventions.

Strengths

  1. Consistent Type Safety:

    • Proper TypeScript types added in code-editor.tsx:93-99 for ThemeProps
    • Consistent interface across all components
  2. Good Conditional Rendering Logic:

    • Smart fallback hierarchy: fullLogoUrllogoUrl → no logo in emails.ts:56-63, 94-101
    • Prevents broken image displays
  3. Proper Data Flow:

    • Logo URLs correctly passed from project data through the entire rendering chain
    • Consistent prop naming (logoUrl, fullLogoUrl, projectDisplayName)
  4. Security:

    • Uses null coalescing operator (??) to handle undefined values safely
    • No XSS risks introduced

⚠️ Issues & Concerns

Code Quality Issues

  1. Duplicate Import in emails.ts:45-46:

    import { Html, Head, Tailwind, Body, Container, Link } from '@react-email/components';
    import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';

    The first import is redundant and should be removed.

  2. Missing Alt Text Accessibility:

    • Hard-coded alt text "Logo" and "Full Logo" in emails.ts:58, 60, 95, 98
    • Should use dynamic alt text like alt={${projectDisplayName} logo} for better accessibility
  3. Inconsistent Styling:

    • Dark theme missing text color for project name: emails.ts:99 should have text-white class
    • Light theme h2 element lacks styling classes

Potential Bugs

  1. Unsafe Logo URL Handling:

    • No validation that logoUrl/fullLogoUrl are valid URLs
    • Could potentially break email rendering if invalid URLs are provided
    • Consider adding URL validation or error handling
  2. Missing Preview Props:

    • Email themes have PreviewProps but don't include logo URLs for preview mode
    • Should add preview logo URLs to emails.ts:78-80, 116-118

Performance Considerations

  1. Image Loading:

    • No lazy loading or size optimization for logo images
    • Consider adding loading="lazy" or size constraints
  2. Multiple Renderings:

    • In send-email/route.tsx:107-115, template is rendered twice when template_id is provided
    • Second rendering includes theme props that first doesn't have - this is intentional but worth noting

🧪 Test Coverage

Missing Test Scenarios:

  • Logo rendering with various URL formats
  • Fallback behavior when logos fail to load
  • Email rendering with missing/invalid logo URLs
  • Theme preview with logo props
  • XSS protection with malicious logo URLs

📋 Recommendations

  1. Fix duplicate import in emails.ts:45
  2. Improve accessibility with dynamic alt text
  3. Add logo URL validation or error handling
  4. Include logo URLs in preview props for better development experience
  5. Consider adding tests for logo rendering scenarios
  6. Add CSS classes for consistent styling in both themes

🎯 Overall Assessment

The implementation is solid and well-integrated with existing patterns. The main concerns are minor code quality issues and missing edge case handling. The feature works as intended and maintains type safety throughout the application.

Recommendation: ✅ Approve with minor fixes suggested


Copy link

recurseml bot commented Aug 19, 2025

Review by RecurseML

🔍 Review performed on 933c60d..7bce171

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (5)

packages/stack-shared/src/helpers/emails.ts
apps/backend/src/lib/email-rendering.tsx
apps/backend/src/app/api/latest/emails/send-email/route.tsx
apps/backend/src/app/api/latest/emails/render-email/route.tsx
apps/dashboard/src/components/vibe-coding/code-editor.tsx

Need help? Join our Discord

@@ -43,15 +43,24 @@ export const emptyEmailTheme = deindent`
`;

export const LightEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link } from '@react-email/components';
import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicate import in LightEmailTheme string literal: remove the redundant import and combine required modules (include Img in a single import).

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This review covers only the changes made since the last review (commit 7bce171), not the entire PR.

This PR adds logo support to Stack Auth's email template system, enabling projects to display their branding in email communications. The implementation introduces a new themeProps object structure that replaces the simple unsubscribeLink parameter across the email rendering pipeline.

Key changes include:

  1. Email Rendering Pipeline: Updated renderEmailWithTemplate() in email-rendering.tsx to accept a comprehensive themeProps object containing unsubscribeLink, projectDisplayName, logoUrl, and fullLogoUrl. This props object is properly spread to the EmailTheme component during rendering.

  2. API Route Updates: Both render-email/route.tsx and send-email/route.tsx now construct themeProps from the tenancy project configuration, extracting logo URLs and display names from tenancy.project.logo_url, tenancy.project.full_logo_url, and tenancy.project.display_name.

  3. Email Theme Templates: The LightEmailTheme and DarkEmailTheme in emails.ts now include conditional logo rendering logic. They prioritize displaying a full logo (fullLogoUrl) if available, otherwise show a smaller logo alongside the project name (logoUrl + projectDisplayName).

  4. Type Safety: Extended the ThemeProps type definition in code-editor.tsx to include the new optional logo properties, ensuring proper TypeScript intellisense for developers writing custom email templates.

The implementation maintains backward compatibility by making all logo properties optional and using null coalescing operators to handle missing values. The changes integrate well with the existing email rendering architecture, following established patterns for prop passing and theme customization.

Confidence score: 3/5

  • This PR has implementation issues that should be addressed before merging, particularly duplicate imports in email themes
  • Score reflects code quality concerns and potential runtime issues with malformed template strings
  • Pay close attention to packages/stack-shared/src/helpers/emails.ts which contains duplicate import statements that will cause compilation errors

5 files reviewed, 1 comment

Edit Code Review Bot Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link

claude bot commented Aug 19, 2025

Claude finished @fomalhautb's task —— View job


Pull Request Review

Reviewing the "Email templates with logo" changes for code quality, security, performance, and potential issues.

Todo List

  • Read and analyze all 5 changed files
  • Review code quality and best practices
  • Check for potential bugs and security issues
  • Assess performance considerations
  • Evaluate test coverage implications
  • Provide comprehensive feedback with specific references

Review Summary

This PR adds logo support to email templates by extending the theme props to include logoUrl and fullLogoUrl properties. The changes are well-structured and follow existing patterns in the codebase.

Positive Aspects

  1. Consistent Implementation: Logo support is properly threaded through all relevant layers:

    • API endpoints (render-email/route.tsx, send-email/route.tsx)
    • Email rendering logic (email-rendering.tsx)
    • TypeScript definitions (code-editor.tsx, emails.ts)
  2. Good Fallback Logic: The theme templates handle logo display gracefully:

    • fullLogoUrl takes precedence over logoUrl
    • Falls back to project display name when no logo is available
    • Proper conditional rendering prevents empty logo elements
  3. Type Safety: TypeScript definitions are properly updated in both the @stackframe/emails module declaration and ThemeProps interface.

⚠️ Issues & Recommendations

Code Quality Issues

  1. Duplicate Import Statement in packages/stack-shared/src/helpers/emails.ts:45-46:

    import { Html, Head, Tailwind, Body, Container, Link } from '@react-email/components';
    import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';

    The first import is redundant and should be removed.

  2. Missing Alt Text Validation: While alt attributes are provided, there's no validation that logoUrl and fullLogoUrl are valid URLs or accessible images. Consider adding basic URL validation.

Security Considerations

  1. URL Validation Missing: The logo URLs are passed directly to <Img> components without validation. While this follows existing patterns in the codebase, consider:
    • Adding URL format validation
    • Sanitizing URLs to prevent potential XSS (though React provides some protection)
    • Consider if there should be allowlisting of domains for logo URLs

Performance Considerations

  1. Image Loading: No optimization for image loading is implemented:

    • No lazy loading attributes
    • No width/height hints for layout stability
    • Consider adding image optimization hints
  2. Double Rendering: In send-email/route.tsx:107-115, when using template_id, the email is rendered twice - once to get the notification category and again for the final send. This is inefficient but appears to be existing behavior, not introduced by this PR.

Minor Issues

  1. Styling Inconsistency: The logo styling differs between light and dark themes:
    • Light theme: className="h-16" for full logo, className="h-8" for regular logo
    • Dark theme: Same classes but different text color handling
    • This is consistent but consider documenting the sizing choices

Test Coverage Recommendations

  1. Missing Test Scenarios:
    • Logo rendering with valid logoUrl
    • Logo rendering with valid fullLogoUrl
    • Fallback behavior when logos are undefined/null
    • Email rendering with both logo types simultaneously
    • Logo URL validation edge cases

Code Locations

  • Logo props added to theme props: apps/backend/src/app/api/latest/emails/render-email/route.tsx:58-62
  • Email sending with logo props: apps/backend/src/app/api/latest/emails/send-email/route.tsx:158-163
  • Theme template updates: packages/stack-shared/src/helpers/emails.ts:49-63, 87-101
  • TypeScript definitions: apps/dashboard/src/components/vibe-coding/code-editor.tsx:97-98

Recommended Actions

  1. Fix duplicate import in emails.ts:45-46
  2. Add basic URL validation for logo URLs
  3. Add tests for logo rendering scenarios
  4. Consider image optimization attributes for better performance
  5. Document logo sizing decisions in code comments

Overall, this is a solid implementation that follows established patterns. The main concern is the duplicate import that should be fixed before merging.


Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Copy link

claude bot commented Aug 19, 2025

Claude finished @fomalhautb's task —— View job


Pull Request Review

Reviewing the "Email templates with logo" changes for code quality, security, performance, and potential issues.

Todo List

  • Read and analyze all 5 changed files
  • Review code quality and best practices
  • Check for potential bugs and security issues
  • Assess performance considerations
  • Evaluate test coverage implications
  • Provide comprehensive feedback with specific references

Review Summary

This PR adds logo support to email templates by extending the theme props to include logoUrl, fullLogoUrl, and projectDisplayName properties. The implementation is well-structured and follows existing patterns in the codebase.

Positive Aspects

  1. Consistent Implementation: Logo support is properly threaded through all relevant layers:

    • API endpoints (render-email/route.tsx:58-62, send-email/route.tsx:158-163)
    • Email rendering logic (email-rendering.tsx:52-57, 96)
    • TypeScript definitions (code-editor.tsx:93-99, emails.ts)
  2. Good Fallback Logic: The theme templates handle logo display gracefully:

    • fullLogoUrl takes precedence over logoUrl (emails.ts:56-63, 94-101)
    • Falls back to project display name when no logo is available
    • Proper conditional rendering prevents empty logo elements
  3. Type Safety: TypeScript definitions are properly updated in both the @stackframe/emails module declaration and ThemeProps interface.

⚠️ Issues & Recommendations

Critical Code Quality Issue

  1. Duplicate Import Statement in packages/stack-shared/src/helpers/emails.ts:45-47:
    export const LightEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
    import { ThemeProps } from "@stackframe/emails"
    import { ThemeProps } from "@stackframe/emails"
    Issue: Line 47 duplicates the import from line 46. This will cause a compilation error.
    Fix: Remove the duplicate import on line 47.

Security Considerations

  1. URL Validation Missing: The logo URLs are passed directly to <Img> components without validation:
    • Consider adding basic URL format validation
    • While React provides some XSS protection, consider sanitizing URLs
    • Evaluate if there should be domain allowlisting for logo URLs

Performance Considerations

  1. Image Loading: No optimization for image loading is implemented:

    • No lazy loading attributes
    • No width/height hints for layout stability
    • Consider adding image optimization hints for better performance
  2. Double Rendering: In send-email/route.tsx:107-115, when using template_id, the email is rendered twice - once to get the notification category and again for the final send. This is existing behavior but worth noting for efficiency.

Minor Issues

  1. Preview Props Inconsistency: The PreviewProps in both themes (emails.ts:78-80, 116-118) only include unsubscribeLink but don't include the new logo properties. Consider adding preview logo URLs for better development experience.

  2. Alt Text: Hard-coded alt text "Logo" and "Full Logo" could be more descriptive. Consider using dynamic alt text like alt={\${projectDisplayName} logo`}` for better accessibility.

Test Coverage Recommendations

Missing Test Scenarios:

  • Logo rendering with valid logoUrl and fullLogoUrl
  • Fallback behavior when logos are undefined/null
  • Email rendering with both logo types simultaneously
  • Theme preview with logo props
  • Logo URL edge cases (invalid URLs, malformed data)

Specific Code References

  • Logo props added to render endpoint: apps/backend/src/app/api/latest/emails/render-email/route.tsx:58-62
  • Logo props added to send endpoint: apps/backend/src/app/api/latest/emails/send-email/route.tsx:158-163
  • Theme props extended in email rendering: apps/backend/src/lib/email-rendering.tsx:52-57, 96
  • TypeScript definitions updated: apps/dashboard/src/components/vibe-coding/code-editor.tsx:93-99
  • Template implementations: packages/stack-shared/src/helpers/emails.ts:49-63, 87-101

Recommendation

Approve with minor fixes required

The main blocker is the duplicate import that will cause compilation errors. Once fixed, this is a solid implementation that follows established patterns and maintains type safety throughout the application.

Priority Actions:

  1. Fix the duplicate import in emails.ts:47 (critical)
  2. Add logo URL validation (recommended)
  3. Consider adding preview logo props (nice to have)
  4. Add tests for logo scenarios (recommended)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/stack-shared/src/helpers/emails.ts (1)

45-46: Duplicate imports inside LightEmailTheme string cause syntax error

There are two import lines for @react-email/components inside the template string. This will break bundling.

Apply this dedupe:

-export const LightEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link } from '@react-email/components';
-import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
+export const LightEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
apps/backend/src/lib/email-rendering.tsx (1)

95-101: Fix: spreading possibly undefined themeProps throws at runtime; also set correct merge precedence

If options.themeProps is undefined, JSON.stringify(options.themeProps) becomes undefined and {...undefined} throws. Also, PreviewProps should provide defaults that call-site props can override, not the other way around.

Apply:

-        const themeProps = {
-          ...${JSON.stringify(options.themeProps)},
-          ...${previewMode ? "EmailTheme.PreviewProps" : "{}"},
-        }
+        const themeProps = {
+          ...${previewMode ? "EmailTheme.PreviewProps" : "{}"},
+          ...${JSON.stringify(options.themeProps || {})},
+        }

This unblocks first-pass renders (which don’t pass themeProps) in send-email and prevents PreviewProps from clobbering explicit props.

🧹 Nitpick comments (5)
packages/stack-shared/src/helpers/emails.ts (5)

47-47: Prefer type-only import for ThemeProps to avoid runtime import

Using a value import for a type may keep an unnecessary import after transpilation depending on settings.

-import { ThemeProps } from "@stackframe/emails"
+import type { ThemeProps } from "@stackframe/emails"

56-63: Add text fallback and better alt text for accessibility

If neither logo is set, nothing renders; also alt text should be descriptive.

-            {fullLogoUrl ? 
-              <Img src={fullLogoUrl} alt="Full Logo" className="h-16" /> :
-              logoUrl ? 
-                <div className="flex gap-2 items-center">
-                  <Img src={logoUrl} alt="Logo" className="h-8" />
-                  <h2>{projectDisplayName}</h2>
-                </div>
-                : null}
+            {fullLogoUrl ? (
+              <Img src={fullLogoUrl} alt={(projectDisplayName ? projectDisplayName + " " : "") + "full logo"} className="h-16" />
+            ) : logoUrl ? (
+              <div className="flex gap-2 items-center">
+                <Img src={logoUrl} alt={(projectDisplayName ? projectDisplayName + " " : "") + "logo"} className="h-8" />
+                <h2>{projectDisplayName}</h2>
+              </div>
+            ) : (
+              projectDisplayName ? <h2>{projectDisplayName}</h2> : null
+            )}

79-80: Consider enriching PreviewProps to showcase branding in preview

Optional: add sample projectDisplayName/logoUrl to improve preview experience in the editor.

 EmailTheme.PreviewProps = {
-  unsubscribeLink: "https://example.com",
+  unsubscribeLink: "https://example.com",
+  projectDisplayName: "Acme Inc.",
+  // logoUrl: "https://example.com/logo.png",
+  // fullLogoUrl: "https://example.com/full-logo.png",
 } satisfies Partial<ThemeProps>

84-85: Prefer type-only import for ThemeProps in Dark theme

Same rationale as Light theme.

-const DarkEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
-import { ThemeProps } from "@stackframe/emails"
+const DarkEmailTheme = `import { Html, Head, Tailwind, Body, Container, Link, Img } from '@react-email/components';
+import type { ThemeProps } from "@stackframe/emails"

94-101: Dark theme: add text fallback and descriptive alt text

Mirror Light theme’s accessibility improvements.

-            {fullLogoUrl ? 
-              <Img src={fullLogoUrl} alt="Full Logo" className="h-16" /> :
-              logoUrl ? 
-                <div className="flex gap-2 items-center">
-                  <Img src={logoUrl} alt="Logo" className="h-8" />
-                  <h2 className="text-white">{projectDisplayName}</h2>
-                </div>
-                : null}
+            {fullLogoUrl ? (
+              <Img src={fullLogoUrl} alt={(projectDisplayName ? projectDisplayName + " " : "") + "full logo"} className="h-16" />
+            ) : logoUrl ? (
+              <div className="flex gap-2 items-center">
+                <Img src={logoUrl} alt={(projectDisplayName ? projectDisplayName + " " : "") + "logo"} className="h-8" />
+                <h2 className="text-white">{projectDisplayName}</h2>
+              </div>
+            ) : (
+              projectDisplayName ? <h2 className="text-white">{projectDisplayName}</h2> : null
+            )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 933c60d and 46e8c98.

📒 Files selected for processing (5)
  • apps/backend/src/app/api/latest/emails/render-email/route.tsx (1 hunks)
  • apps/backend/src/app/api/latest/emails/send-email/route.tsx (2 hunks)
  • apps/backend/src/lib/email-rendering.tsx (3 hunks)
  • apps/dashboard/src/components/vibe-coding/code-editor.tsx (1 hunks)
  • packages/stack-shared/src/helpers/emails.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/backend/src/app/api/latest/**/*

📄 CodeRabbit Inference Engine (CLAUDE.md)

apps/backend/src/app/api/latest/**/*: Main API routes are located in /apps/backend/src/app/api/latest
The project uses a custom route handler system in the backend for consistent API responses

Files:

  • apps/backend/src/app/api/latest/emails/render-email/route.tsx
  • apps/backend/src/app/api/latest/emails/send-email/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: docker
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: all-good
  • GitHub Check: Security Check
🔇 Additional comments (7)
apps/backend/src/app/api/latest/emails/render-email/route.tsx (2)

58-62: Branding props wiring looks good

Passing projectDisplayName, logoUrl, and fullLogoUrl via themeProps aligns with the new renderer signature and EmailTheme expectations. No issues here.


58-62: Sanity-check logo URLs are absolute and reachable

If tenancy.project.logo_url/full_logo_url can be relative or point to private hosts, many email clients will fail to display them. Ensure these values are absolute, public URLs, or normalize/validate before passing through.

Would you like a guard that validates these URLs and drops invalid ones to avoid broken images?

apps/dashboard/src/components/vibe-coding/code-editor.tsx (1)

96-99: Type augmentation matches backend themeProps

Adding projectDisplayName, logoUrl, and fullLogoUrl to ThemeProps keeps Monaco typings in sync with the renderer and themes. Looks good.

apps/backend/src/app/api/latest/emails/send-email/route.tsx (3)

158-163: Good: themeProps passed with unsubscribe and branding

This aligns with the updated renderEmailWithTemplate signature and new EmailTheme props.


107-115: First-pass render doesn’t pass themeProps; relies on renderer to handle undefined

The first pass to compute notification category omits themeProps. With the current renderer implementation, object-spreading an undefined themeProps causes a runtime error. Ensure the renderer defaults themeProps to {} when absent (see email-rendering.tsx review).

Do you want me to open a follow-up to harden renderEmailWithTemplate and/or pass branding themeProps in the first pass too?


6-6: KnownErrors correctly re-exported at package root

The packages/stack-shared/src/index.ts file includes:

export {
  KnownError,
  KnownErrors
} from "./known-errors";

so importing KnownErrors from "@stackframe/stack-shared" will resolve successfully at runtime.

apps/backend/src/lib/email-rendering.tsx (1)

52-57: Public API extension is coherent

Adding themeProps with unsubscribeLink, projectDisplayName, logoUrl, and fullLogoUrl matches consuming routes and theme templates.

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.

2 participants