No project description provided
Project description
mountaineer-email
Dependencies to easily format and send email with Mountaineer or FastAPI.
Getting Started
Since email deliverability is nearly zero if you send with local linux utilities, you'll almost always want to use a 3rd party service. This package is provider agnostic and delegates delivery integrations to mountaineer-cloud provider packages such as Resend.
TODO: Full example
Designing
You want your emails to be beautiful, but email design is notoriously a headache. Email clients lag significantly in adoption of html features and only implement a subset of the CSS spec. If it gives you any sense of the current ecosystem, marking up with <table> still rules the day. See the currently supported css attributes, for reference.
For complex email templates, you'll probably want to use a dedicated designer app or plugin for something like Figma. For simpler email layouts we bundle basic Tailwind support by inlining the CSS markup that are usually defined in classes.
Usage
To setup a new email, you'll need both the view (equivalent to a Mountaineer frontend view) and a controller (similarly equivalent to a Mountaineer controller). A typical project layout looks like:
myproject/
├── controllers/
├── emails/
│ └── email1.py
└── views/
├── app/
├── emails/
│ ├── email1/
│ │ └── page.tsx
│ └── template.tsx
└── project.json
This layout mirrors the frontend views exactly - we support individual pages and the nesting of layouts to wrap your emails in a common design.
Unlike your conventional routes, emails aren't interactive. You can think of them as running without javascript within an email client. So the initial representation of your React component will be the permanent representation of the page.
We compile down your React components into raw html using Mountaineer's regular SSR renderer. We then perform some email-specific transformations that allow your styling to show up properly for browsers.
Define your view:
import React from "react";
import { useServer } from "./_server/useServer";
const Page = () => {
const serverState = useServer();
return (
<div className="space-y-4">
{serverState.user_name && <div>Hi {serverState.user_name}!</div>}
</div>
);
};
export default Page;
And then your associated controller:
from uuid import UUID
from fastapi import Depends
from mountaineer_email import EmailControllerBase, EmailMetadata, EmailRenderBase
from pydantic import BaseModel
from iceaxe import DBConnection
from mountaineer import CoreDependencies, LinkAttribute, ManagedViewPath, Metadata
from iceaxe.mountaineer import DatabaseDependencies
from myproject import models
class WelcomeEmailRequest(BaseModel):
user_id: UUID
class WelcomeEmailRender(EmailRenderBase):
user_name: str | None
class WelcomeEmailController(EmailControllerBase[WelcomeEmailRequest]):
view_path = "emails/welcome/page.tsx"
async def render(
self,
payload: WelcomeEmailRequest,
db_session: DBConnection = Depends(DatabaseDependencies.get_db_connection),
) -> WelcomeEmailRender:
user = await db_session.get(models.User, payload.user_id)
if not user:
raise ValueError(f"User not found: {payload.user_id}")
return WelcomeEmailRender(
user_name=user.name,
email_metadata=EmailMetadata(
subject="Welcome!",
to_email=user.email,
),
metadata=Metadata(
links=[LinkAttribute(rel="stylesheet", href="/static/auth_main.css")]
),
)
Then add these controllers to your AppController:
from myproject import emails
controller = AppController(
config=config,
global_metadata=Metadata(
links=[LinkAttribute(rel="stylesheet", href="/static/app_main.css")]
),
custom_builders=[
PostCSSBundler(),
],
)
controller.register(emails.WelcomeEmailController())
mountaineer-email only owns the rendering and preview flow. Provider-specific delivery settings should come from the matching mountaineer-cloud provider config, for example ResendConfig if you're sending through Resend.
Inliner
To inline Tailwind (similar to the juice package), we recommend you use @react-email/tailwind since it has a lot of helper utilities out of the box for tailwind's variables:
cd project/views && npm install @react-email/tailwind
import { Tailwind } from "@react-email/tailwind";
const Email = () => {
return (
<Tailwind>
<button className="bg-blue-500">Click me!</button>
</Tailwind>
);
};
export default Email;
Admin Panel
We bundle an admin panel that allows you to preview your emails with different imports. You'll have to add these explicitly to your AppController. We suggest conditionally adding these to your webservice if you're running locally:
import mountaineer_email.controllers as email_admin_controllers
if ENV == "development":
controller.register(email_admin_controllers.EmailHomeController())
controller.register(email_admin_controllers.EmailDetailController())
Development
If you update the admin UI files, you'll need to build the artifacts for inclusion in the published library. We do this automatically when distributing through CI, so this is just when you're making changes and testing locally:
uv run build-email
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mountaineer_email-0.1.0.tar.gz.
File metadata
- Download URL: mountaineer_email-0.1.0.tar.gz
- Upload date:
- Size: 749.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e48fff29aeeb173812d1f532eb27512135af0273e3bbc425fb6781051ceb022
|
|
| MD5 |
29d3d83984bab69ac116bd9f65b9b1ef
|
|
| BLAKE2b-256 |
622b7c852dbb6fb2df8ac0fcfd95072076db246ae0e604d9ed807c0ac1bf4373
|
File details
Details for the file mountaineer_email-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mountaineer_email-0.1.0-py3-none-any.whl
- Upload date:
- Size: 561.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0bd9b28dbe59f755e324de901d9f463273312cd78ed3ee78e7b4fb23adc35c2
|
|
| MD5 |
0e615481abb279845efd5cc432a1d665
|
|
| BLAKE2b-256 |
1ada042f7101d777f944528e197d660dd0401ff85e76d3ae2c0889b4b3fe6e90
|