skills/ballee/supabase-email-templates/SKILL.md
Generate and deploy Supabase Auth email templates with hosted logos for reliable display; use when email images aren't displaying, updating email content/styling, or deploying template changes
npx skillsauth add javeedishaq/ai-workflow-orchestrator supabase-email-templatesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Manage Supabase Auth email templates with hosted logos for reliable display across all email clients.
Use this skill when:
DO NOT use for:
# Generate templates locally (creates HTML from React Email)
pnpm email:templates:generate
# Deploy templates to production
pnpm email:templates:deploy
# Deploy to staging
pnpm email:templates:deploy --staging
# Verify current production templates
pnpm email:templates:deploy --verify
# Full workflow (generate + deploy)
pnpm email:templates:update
Base64-encoded images are blocked by Gmail and Outlook (majority of email clients). Research shows:
| Method | Gmail | Outlook | Apple Mail | Email Size | |--------|-------|---------|------------|------------| | Hosted (recommended) | Works | Works | Works | Small | | Base64 | Blocked | Blocked | Works | +30% bloat | | CID embedded | Fails | Works | Works | Increases |
Solution: Use hosted images on flow.ballee.app:
Logo hosted at: https://flow.ballee.app/images/ballee-logo-email.png
Source file: apps/web/public/images/ballee-logo-email.png (120x120px PNG)
Defined in: packages/email-templates/src/components/logo.tsx
export const DEFAULT_LOGO_URL =
'https://flow.ballee.app/images/ballee-logo-email.png';
| Template | Supabase API Field | Local File |
|----------|-------------------|------------|
| Confirm Signup | mailer_templates_confirmation_content | confirm-signup.html |
| Invite User | mailer_templates_invite_content | invite-user.html |
| Magic Link | mailer_templates_magic_link_content | magic-link.html |
| Change Email | mailer_templates_email_change_content | change-email-address.html |
| Reset Password | mailer_templates_recovery_content | reset-password.html |
| Reauthentication | mailer_templates_reauthentication_content | reauthentication.html |
packages/email-templates/src/
├── components/
│ ├── logo.tsx # Shared logo component (hosted URL)
│ └── wrapper.tsx # Email wrapper (includes logo by default)
├── emails/
│ ├── auth-base.email.tsx # Base template for auth emails
│ ├── confirm-signup.email.tsx
│ ├── invite-user.email.tsx
│ └── ... # Other email templates
└── lib/
└── colors.ts # Brand colors
scripts/
├── generate-supabase-templates.tsx # Generates HTML from React Email
└── update-supabase-email-templates.sh # Deploys to Supabase via API
supabase-email-templates/ # Generated HTML files (gitignored)
├── confirm-signup.html
├── invite-user.html
└── ...
# Replace the logo file
cp new-logo.png apps/web/public/images/ballee-logo-email.png
flow.ballee.app)pnpm email:templates:update
If hosting location changes, update packages/email-templates/src/components/logo.tsx:
export const DEFAULT_LOGO_URL =
'https://new-domain.com/path/to/logo.png';
Then regenerate and deploy templates.
Endpoint: https://api.supabase.com/v1/projects/{project_ref}/config/auth
Method: PATCH
Authentication: Bearer token from 1Password (uipc6jse6q32hu3nyfh6qmssoq)
Project References:
csjruhqyqzzqxnfeyiafhxpcknyqswetsqmqmeepSymptom: Email received but logo shows broken image or placeholder
Possible Causes:
Solution:
# 1. Verify logo file exists on production
curl -I https://flow.ballee.app/images/ballee-logo-email.png
# 2. Verify templates use correct URL
pnpm email:templates:deploy --verify
# 3. If needed, regenerate and deploy
pnpm email:templates:update
Symptom: Email shows {{ .Email }} instead of actual email address
Cause: URL-encoded template variables
Solution: The generation script automatically fixes these. Regenerate:
pnpm email:templates:generate
Symptom: "Could not retrieve Supabase access token"
Solution:
# Login to 1Password CLI
op signin
# Verify token exists
op item get uipc6jse6q32hu3nyfh6qmssoq --fields password --reveal
Symptom: "Error updating templates" with API response
Common Causes:
op signinFor local development, email templates are configured in apps/web/supabase/config.toml:
[auth.email.template.recovery]
subject = "Reset your password - Ballee"
content_path = "./supabase/templates/reset-password.html"
Local templates use file paths and are loaded when Supabase starts. Changes require:
pnpm supabase:web:stop && pnpm supabase:web:start
| File | Purpose |
|------|---------|
| packages/email-templates/src/components/logo.tsx | Shared logo component |
| packages/email-templates/src/components/wrapper.tsx | Email wrapper with logo |
| packages/email-templates/src/emails/auth-base.email.tsx | Base template for auth emails |
| apps/web/public/images/ballee-logo-email.png | Logo image file (120x120px) |
| scripts/generate-supabase-templates.tsx | Template generator script |
| scripts/update-supabase-email-templates.sh | Production deployment script |
Scenario: User reports logo not showing in password reset email
# 1. Verify logo file is accessible
curl -I https://flow.ballee.app/images/ballee-logo-email.png
# Should return HTTP 200
# 2. Verify current templates
pnpm email:templates:deploy --verify
# Output shows: ✅ recovery: hosted image - src="https://flow.ballee.app/..."
# 3. If templates correct, explain to user that email clients may block images
# by default. They need to click "Display images" or add sender to safe list.
# 4. If templates incorrect, regenerate and deploy
pnpm email:templates:update
tools
# Test Patterns Testing patterns for reliable, maintainable, and fast tests. > **Template Usage:** Customize for your test framework (Vitest, Jest, Playwright, etc.) and assertion library. ## Test Structure ```typescript // user.test.ts import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { userService } from '@/services/user.service'; import { createTestUser, cleanupTestData } from '@/tests/helpers'; describe('UserService', () => { let testUserId: string; befor
tools
# State Management Patterns Client-side state management patterns for modern applications. > **Template Usage:** Customize for your state library (React Query, Zustand, Jotai, Redux, etc.). ## State Categories | Type | Description | Solution | |------|-------------|----------| | **Server State** | Data from API/database | React Query, SWR | | **Client State** | UI state, user preferences | Zustand, Jotai, useState | | **Form State** | Form inputs, validation | React Hook Form, Formik | | **U
development
# Service Patterns Service layer patterns for clean architecture with proper error handling, logging, and type safety. > **Template Usage:** Customize for your ORM (Prisma, Drizzle, TypeORM, etc.) and logging solution. ## Result Type Pattern Never throw exceptions from services. Always return a Result type. ```typescript // lib/result.ts export type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; export function ok<T>(data: T): Result<T, never> { r
testing
# Row-Level Security Patterns Database security patterns for multi-tenant and user-scoped data. > **Template Usage:** Customize for your database (PostgreSQL, Supabase, etc.) and auth system. ## RLS Fundamentals ### Enable RLS on Tables ```sql -- Enable RLS (required before policies take effect) ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE posts ENABLE ROW LEVEL SECURITY; ALTER TABLE comments ENABLE ROW LEVEL SECURITY; -- Force RLS for table owners too (recommended) ALTER TABLE