diff --git a/.gitignore b/.gitignore
index de1430a5..79539fed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,4 +40,6 @@ yarn-error.log*
 # turbo
 .turbo
 
-dist
\ No newline at end of file
+dist
+
+.react-email
\ No newline at end of file
diff --git a/bun.lockb b/bun.lockb
index ba0556b6..358e28ac 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/examples/email/emails/static/vercel-arrow.png b/examples/email/emails/static/vercel-arrow.png
new file mode 100644
index 00000000..018f64d2
Binary files /dev/null and b/examples/email/emails/static/vercel-arrow.png differ
diff --git a/examples/email/emails/static/vercel-logo.png b/examples/email/emails/static/vercel-logo.png
new file mode 100644
index 00000000..5b970948
Binary files /dev/null and b/examples/email/emails/static/vercel-logo.png differ
diff --git a/examples/email/emails/static/vercel-team.png b/examples/email/emails/static/vercel-team.png
new file mode 100644
index 00000000..d3de7d93
Binary files /dev/null and b/examples/email/emails/static/vercel-team.png differ
diff --git a/examples/email/emails/static/vercel-user.png b/examples/email/emails/static/vercel-user.png
new file mode 100644
index 00000000..81beac69
Binary files /dev/null and b/examples/email/emails/static/vercel-user.png differ
diff --git a/examples/email/emails/vercel-invite-user.tsx b/examples/email/emails/vercel-invite-user.tsx
new file mode 100644
index 00000000..276f1800
--- /dev/null
+++ b/examples/email/emails/vercel-invite-user.tsx
@@ -0,0 +1,148 @@
+import { setupI18n } from "@languine/react-email";
+import {
+  Body,
+  Button,
+  Column,
+  Container,
+  Head,
+  Heading,
+  Hr,
+  Html,
+  Img,
+  Link,
+  Preview,
+  Row,
+  Section,
+  Tailwind,
+  Text,
+} from "@react-email/components";
+import * as React from "react";
+
+interface VercelInviteUserEmailProps {
+  locale: string;
+  username?: string;
+  userImage?: string;
+  invitedByUsername?: string;
+  invitedByEmail?: string;
+  teamName?: string;
+  teamImage?: string;
+  inviteLink?: string;
+  inviteFromIp?: string;
+  inviteFromLocation?: string;
+}
+
+const baseUrl = process.env.VERCEL_URL
+  ? `https://${process.env.VERCEL_URL}`
+  : "";
+
+export const VercelInviteUserEmail = ({
+  locale = "es",
+  username = "alanturing",
+  userImage = `${baseUrl}/static/vercel-user.png`,
+  invitedByUsername = "Alan",
+  invitedByEmail = "alan.turing@example.com",
+  teamName = "Enigma",
+  teamImage = `${baseUrl}/static/vercel-team.png`,
+  inviteLink = "https://vercel.com/teams/invite/foo",
+  inviteFromIp = "204.13.186.218",
+  inviteFromLocation = "São Paulo, Brazil",
+}: VercelInviteUserEmailProps) => {
+  const i18n = setupI18n(locale);
+
+  return (
+    <Html>
+      <Head />
+      <Preview>{i18n.t("previewText", { invitedByUsername })}</Preview>
+      <Tailwind>
+        <Body className="bg-white my-auto mx-auto font-sans px-2">
+          <Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">
+            <Section className="mt-[32px]">
+              <Img
+                src={`${baseUrl}/static/vercel-logo.png`}
+                width="40"
+                height="37"
+                alt={i18n.t("logoAlt")}
+                className="my-0 mx-auto"
+              />
+            </Section>
+            <Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
+              {i18n.t("joinTeamHeading", {
+                teamName: teamName,
+                company: "Vercel",
+              })}
+            </Heading>
+            <Text className="text-black text-[14px] leading-[24px]">
+              {i18n.t("greeting", { username })}
+            </Text>
+            <Text className="text-black text-[14px] leading-[24px]">
+              {i18n.t("invitationText", {
+                invitedByUsername: <strong>{invitedByUsername}</strong>,
+                email: (
+                  <Link href={`mailto:${invitedByEmail}`}>
+                    {invitedByEmail}
+                  </Link>
+                ),
+                teamName: <strong>{teamName}</strong>,
+                company: "Vercel",
+              })}
+            </Text>
+            <Section>
+              <Row>
+                <Column align="right">
+                  <Img
+                    className="rounded-full"
+                    src={userImage}
+                    width="64"
+                    height="64"
+                  />
+                </Column>
+                <Column align="center">
+                  <Img
+                    src={`${baseUrl}/static/vercel-arrow.png`}
+                    width="12"
+                    height="9"
+                    alt={i18n.t("invitedToAlt")}
+                  />
+                </Column>
+                <Column align="left">
+                  <Img
+                    className="rounded-full"
+                    src={teamImage}
+                    width="64"
+                    height="64"
+                  />
+                </Column>
+              </Row>
+            </Section>
+            <Section className="text-center mt-[32px] mb-[32px]">
+              <Button
+                className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
+                href={inviteLink}
+              >
+                {i18n.t("joinTeamButton")}
+              </Button>
+            </Section>
+            <Text className="text-black text-[14px] leading-[24px]">
+              {i18n.t("copyUrlText")}{" "}
+              <Link href={inviteLink} className="text-blue-600 no-underline">
+                {inviteLink}
+              </Link>
+            </Text>
+            <Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
+            <Text className="text-[#666666] text-[12px] leading-[24px]">
+              {i18n.t("footerText", {
+                username: <span className="text-black">{username}</span>,
+                ip: <span className="text-black">{inviteFromIp}</span>,
+                location: (
+                  <span className="text-black">{inviteFromLocation}</span>
+                ),
+              })}
+            </Text>
+          </Container>
+        </Body>
+      </Tailwind>
+    </Html>
+  );
+};
+
+export default VercelInviteUserEmail;
diff --git a/examples/email/languine.config.ts b/examples/email/languine.config.ts
new file mode 100644
index 00000000..e8e3b3a3
--- /dev/null
+++ b/examples/email/languine.config.ts
@@ -0,0 +1,18 @@
+import { defineConfig } from "languine";
+
+export default defineConfig({
+  version: "7.0.0",
+  locale: {
+    source: "en",
+    targets: ["es", "sv", "pt"],
+  },
+  files: {
+    json: {
+      include: ["locales/[locale].json"],
+    },
+  },
+  llm: {
+    provider: "openai",
+    model: "gpt-4-turbo",
+  },
+});
\ No newline at end of file
diff --git a/examples/email/locales/en.json b/examples/email/locales/en.json
new file mode 100644
index 00000000..8be44dcc
--- /dev/null
+++ b/examples/email/locales/en.json
@@ -0,0 +1,15 @@
+{
+    "previewText": "Join %{invitedByUsername} on %{company}",
+    "company": "%{company}",
+    "logoAlt": "Vercel Logo",
+    "joinTeamHeading": "Join %{teamName} on %{company}",
+    "greeting": "Hi %{username},",
+    "invitationText":
+      "%{invitedByUsername} (%{email}) has invited you to join the %{teamName} team on %{company}.",
+    "invitedToAlt": "Invited to",
+    "joinTeamButton": "Join the team",
+    "copyUrlText": "Or copy and paste this URL into your browser:",
+    "footerText":
+      "This invitation was intended for %{username} (%{ip} from %{location}). If you were not expecting this invitation, you can ignore this email. If you are concerned about your account's safety, please reply to this email to get in touch with us."
+  }
+  
\ No newline at end of file
diff --git a/examples/email/locales/es.json b/examples/email/locales/es.json
new file mode 100644
index 00000000..40bdab2b
--- /dev/null
+++ b/examples/email/locales/es.json
@@ -0,0 +1,12 @@
+{
+  "previewText": "Únete a %{invitedByUsername} en %{company}",
+  "company": "%{company}",
+  "logoAlt": "Logo de Vercel",
+  "joinTeamHeading": "Únete al equipo %{teamName} en %{company}",
+  "greeting": "Hola %{username},",
+  "invitationText": "%{invitedByUsername} (%{email}) te ha invitado a unirte al equipo %{teamName} en %{company}.",
+  "invitedToAlt": "Invitado a",
+  "joinTeamButton": "Únete al equipo",
+  "copyUrlText": "O copia y pega esta URL en tu navegador:",
+  "footerText": "Esta invitación fue destinada para %{username} (%{ip} desde %{location}). Si no esperabas esta invitación, puedes ignorar este correo electrónico. Si te preocupa la seguridad de tu cuenta, por favor responde a este correo electrónico para ponerte en contacto con nosotros."
+}
\ No newline at end of file
diff --git a/examples/email/locales/pt.json b/examples/email/locales/pt.json
new file mode 100644
index 00000000..2bed970f
--- /dev/null
+++ b/examples/email/locales/pt.json
@@ -0,0 +1,12 @@
+{
+  "previewText": "Junte-se a %{invitedByUsername} na %{company}",
+  "company": "%{company}",
+  "logoAlt": "Logotipo da Vercel",
+  "joinTeamHeading": "Junte-se ao time %{teamName} na %{company}",
+  "greeting": "Oi %{username},",
+  "invitationText": "%{invitedByUsername} (%{email}) convidou você para se juntar ao time %{teamName} na %{company}.",
+  "invitedToAlt": "Convidado para",
+  "joinTeamButton": "Entrar no time",
+  "copyUrlText": "Ou copie e cole este URL no seu navegador:",
+  "footerText": "Este convite foi destinado para %{username} (%{ip} de %{location}). Se você não estava esperando este convite, pode ignorar este e-mail. Se estiver preocupado com a segurança da sua conta, por favor responda a este e-mail para entrar em contato conosco."
+}
\ No newline at end of file
diff --git a/examples/email/locales/sv.json b/examples/email/locales/sv.json
new file mode 100644
index 00000000..ecfa1a8d
--- /dev/null
+++ b/examples/email/locales/sv.json
@@ -0,0 +1,12 @@
+{
+  "previewText": "Gå med %{invitedByUsername} på %{company}",
+  "company": "%{company}",
+  "logoAlt": "Vercel-logotyp",
+  "joinTeamHeading": "Gå med i %{teamName} på %{company}",
+  "greeting": "Hej %{username},",
+  "invitationText": "%{invitedByUsername} (%{email}) har bjudit in dig att gå med i %{teamName}-teamet på %{company}.",
+  "invitedToAlt": "Inbjuden till",
+  "joinTeamButton": "Gå med i teamet",
+  "copyUrlText": "Eller kopiera och klistra in denna URL i din webbläsare:",
+  "footerText": "Denna inbjudan var avsedd för %{username} (%{ip} från %{location}). Om du inte förväntade dig denna inbjudan kan du ignorera detta e-postmeddelande. Om du är orolig för säkerheten på ditt konto, vänligen svara på detta e-postmeddelande för att komma i kontakt med oss."
+}
\ No newline at end of file
diff --git a/examples/email/package.json b/examples/email/package.json
new file mode 100644
index 00000000..4dbdab10
--- /dev/null
+++ b/examples/email/package.json
@@ -0,0 +1,20 @@
+{
+  "name": "react-email-starter",
+  "version": "0.1.6",
+  "private": true,
+  "scripts": {
+    "build": "email build",
+    "dev": "email dev",
+    "export": "email export"
+  },
+  "dependencies": {
+    "@react-email/components": "0.0.31",
+    "react": "19.0.0",
+    "react-dom": "19.0.0"
+  },
+  "devDependencies": {
+    "@types/react": "19.0.1",
+    "@types/react-dom": "19.0.1",
+    "react-email": "3.0.4"
+  }
+}
\ No newline at end of file
diff --git a/examples/email/readme.md b/examples/email/readme.md
new file mode 100644
index 00000000..fc875bfa
--- /dev/null
+++ b/examples/email/readme.md
@@ -0,0 +1,27 @@
+# React Email Starter
+
+A live preview right in your browser so you don't need to keep sending real emails during development.
+
+## Getting Started
+
+First, install the dependencies:
+
+```sh
+npm install
+# or
+yarn
+```
+
+Then, run the development server:
+
+```sh
+npm run dev
+# or
+yarn dev
+```
+
+Open [localhost:3000](http://localhost:3000) with your browser to see the result.
+
+## License
+
+MIT License
diff --git a/examples/next-international/languine.config.ts b/examples/next-international/languine.config.ts
index 6adbc53e..ac0346c1 100644
--- a/examples/next-international/languine.config.ts
+++ b/examples/next-international/languine.config.ts
@@ -14,6 +14,5 @@ export default defineConfig({
   llm: {
     provider: "ollama",
     model: "mistral:latest",
-    temperature: 0,
   },
 });
diff --git a/packages/react-email/README.md b/packages/react-email/README.md
new file mode 100644
index 00000000..a45c331f
--- /dev/null
+++ b/packages/react-email/README.md
@@ -0,0 +1,60 @@
+<p align="center">
+  <img src="https://github.com/midday-ai/languine/blob/main/packages/react-email/image.png" />
+</p>
+
+<p align="center">
+  A lightweight i18n library for React email templates.
+</p>
+
+---
+
+```bash
+$ npm install @languine/react-email
+```
+
+## What is this?
+
+This is a lightweight i18n library for React email templates. It is built on top of `i18n-js`.
+
+Automatically included language files are in the `locales` folder.
+
+## How to use
+
+```tsx
+import { setupI18n } from "@languine/react-email";
+
+export function WelcomeEmail({ locale, name }) {
+  const i18n = setupI18n(locale);
+
+  return (
+    <Html>
+      <Head />
+      <Preview>{i18n.t("preview")}</Preview>
+      <Body>
+         <Text>{i18n.t("welcome", { name  })}</Text>
+      </Body>
+    </Html>
+  );
+}
+```
+
+### Rendering the email
+```tsx
+import { render } from '@react-email/render';
+import { WelcomeEmail } from "./emails/welcome";
+
+const html = await render(<WelcomeEmail locale="en" name="John" />, {
+  pretty: true,
+});
+
+console.log(html);
+```
+
+
+## Works together with Languine CLI
+
+Automatically add and translate your email templates with [Languine CLI](https://languine.ai).
+
+```bash
+$ npx languine@latest
+```
diff --git a/packages/react-email/image.png b/packages/react-email/image.png
new file mode 100644
index 00000000..0d584164
Binary files /dev/null and b/packages/react-email/image.png differ
diff --git a/packages/react-email/package.json b/packages/react-email/package.json
new file mode 100644
index 00000000..7d53747e
--- /dev/null
+++ b/packages/react-email/package.json
@@ -0,0 +1,23 @@
+{
+  "name": "@languine/react-email",
+  "version": "0.1.0",
+  "files": ["dist", "README.md"],
+  "main": "dist/index.mjs",
+  "types": "dist/index.d.ts",
+  "license": "MIT",
+  "scripts": {
+    "clean": "rm -rf .turbo node_modules",
+    "lint": "biome check .",
+    "format": "biome format --write .",
+    "typecheck": "tsc --noEmit",
+    "build": "tsup --clean src/index.tsx"
+  },
+  "devDependencies": {
+    "tsup": "^8.3.5",
+    "typescript": "^5.7.2"
+  },
+  "dependencies": {
+    "i18n-js": "^4.5.1",
+    "react-string-replace": "^1.1.1"
+  }
+}
diff --git a/packages/react-email/src/index.tsx b/packages/react-email/src/index.tsx
new file mode 100644
index 00000000..f7c3fb34
--- /dev/null
+++ b/packages/react-email/src/index.tsx
@@ -0,0 +1,21 @@
+import { I18n } from "i18n-js";
+import { interpolate } from "./interpolate";
+import { translations } from "./loader";
+
+export function setupI18n(locale?: string) {
+  if (Object.keys(translations).length === 0) {
+    throw new Error(
+      "No translation files found in locales directory, make sure it's in the root of the package",
+    );
+  }
+
+  const i18n = new I18n(translations);
+
+  // Set locale to first available locale if no locale is provided
+  i18n.locale = locale || Object.keys(translations).at(0) || "en";
+  i18n.enableFallback = true;
+  // @ts-ignore
+  i18n.interpolate = interpolate;
+
+  return i18n;
+}
diff --git a/packages/react-email/src/interpolate.tsx b/packages/react-email/src/interpolate.tsx
new file mode 100644
index 00000000..16edbca9
--- /dev/null
+++ b/packages/react-email/src/interpolate.tsx
@@ -0,0 +1,49 @@
+import type { TranslateOptions } from "i18n-js";
+import type { I18n } from "i18n-js";
+import React from "react";
+import reactStringReplace from "react-string-replace";
+
+export function interpolate(
+  i18n: I18n,
+  message: string,
+  options: TranslateOptions,
+) {
+  const transformedOptions = Object.keys(options).reduce((buffer, key) => {
+    buffer[i18n.transformKey(key)] = options[key];
+    return buffer;
+  }, {} as TranslateOptions);
+
+  return reactStringReplace(message, i18n.placeholder, (match, i) => {
+    let value: React.ReactNode = "";
+    const placeholder = match as string;
+    const name = placeholder.replace(i18n.placeholder, "$1");
+
+    if (transformedOptions[name] != null) {
+      if (React.isValidElement(transformedOptions[name])) {
+        value = transformedOptions[name];
+      } else if (Array.isArray(transformedOptions[name])) {
+        value = transformedOptions[name];
+      } else if (typeof transformedOptions[name] === "object") {
+        value = transformedOptions[name];
+      } else {
+        value = transformedOptions[name].toString().replace(/\$/gm, "_#$#_");
+      }
+    } else if (name in transformedOptions) {
+      value = i18n.nullPlaceholder(
+        i18n,
+        placeholder,
+        message,
+        transformedOptions,
+      );
+    } else {
+      value = i18n.missingPlaceholder(
+        i18n,
+        placeholder,
+        message,
+        transformedOptions,
+      );
+    }
+
+    return <React.Fragment key={i}>{value}</React.Fragment>;
+  });
+}
diff --git a/packages/react-email/src/loader.ts b/packages/react-email/src/loader.ts
new file mode 100644
index 00000000..da08e98e
--- /dev/null
+++ b/packages/react-email/src/loader.ts
@@ -0,0 +1,66 @@
+import fs from "node:fs";
+import path from "node:path";
+
+const translations: Record<string, Record<string, string>> = {};
+
+/**
+ * Recursively searches up directory tree for package.json
+ */
+const findPackageRoot = (dir: string): string => {
+  if (fs.existsSync(path.join(dir, "package.json"))) {
+    return dir;
+  }
+
+  const parentDir = path.dirname(dir);
+  if (parentDir === dir) {
+    throw new Error("Could not find package.json in directory tree");
+  }
+
+  return findPackageRoot(parentDir);
+};
+
+/**
+ * Recursively loads all JSON translation files from locales directory
+ */
+const loadTranslations = (dir: string, baseDir: string) => {
+  const files = fs.readdirSync(dir);
+
+  for (const file of files) {
+    const fullPath = path.join(dir, file);
+    const stat = fs.statSync(fullPath);
+
+    if (stat.isDirectory()) {
+      loadTranslations(fullPath, baseDir);
+      continue;
+    }
+
+    if (!file.endsWith(".json")) {
+      continue;
+    }
+
+    const relativePath = path.relative(baseDir, fullPath);
+    const locale = path.basename(relativePath, ".json");
+    try {
+      const content = fs.readFileSync(fullPath, "utf-8");
+      translations[locale] = JSON.parse(content);
+    } catch (err) {
+      throw new Error(
+        `Failed to load translation file ${fullPath}: ${
+          (err as Error).message
+        }`,
+      );
+    }
+  }
+};
+
+// Load translations from locales directory in package root
+const packageRoot = findPackageRoot(__dirname);
+const localesDir = path.join(packageRoot, "locales");
+
+if (!fs.existsSync(localesDir)) {
+  throw new Error("No locales directory found in package root");
+}
+
+loadTranslations(localesDir, localesDir);
+
+export { translations };
diff --git a/packages/react-email/tsconfig.json b/packages/react-email/tsconfig.json
new file mode 100644
index 00000000..951ae6c1
--- /dev/null
+++ b/packages/react-email/tsconfig.json
@@ -0,0 +1,21 @@
+{
+    "include": ["src/**/*.ts", "src/**/*.tsx"],
+    "compilerOptions": {
+      "target": "esnext",
+      "module": "NodeNext", 
+      "moduleResolution": "nodenext",
+      "allowJs": true,
+      "strict": true,
+      "skipLibCheck": true,
+      "isolatedModules": true,
+      "esModuleInterop": true,
+      "outDir": "dist",
+      "resolveJsonModule": true,
+      "declaration": true,
+      "declarationMap": true,
+      "sourceMap": true,
+      "noEmit": false,
+      "customConditions": ["source"],
+      "jsx": "react-jsx"
+    }
+  }
\ No newline at end of file
diff --git a/packages/react-email/tsup.config.ts b/packages/react-email/tsup.config.ts
new file mode 100644
index 00000000..1e9a253a
--- /dev/null
+++ b/packages/react-email/tsup.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+  dts: true,
+  format: "esm",
+  entry: ["src/index.ts"],
+});