Files
GibSend/apps/marketing/src/app/page.tsx
2025-09-08 20:30:45 +10:00

499 lines
16 KiB
TypeScript

import Image from "next/image";
import Link from "next/link";
import { SiteFooter } from "~/components/SiteFooter";
import { GitHubStarsButton } from "~/components/GitHubStarsButton";
import { Button } from "@usesend/ui/src/button";
import { TopNav } from "~/components/TopNav";
import { FeatureCard } from "~/components/FeatureCard";
import { FeatureCardPlain } from "~/components/FeatureCardPlain";
import { PricingCalculator } from "~/components/PricingCalculator";
import { CodeBlock } from "@usesend/ui/src/code-block";
const REPO = "usesend/usesend";
const REPO_URL = `https://github.com/${REPO}`;
const APP_URL = "https://app.usesend.com";
export default function Page() {
return (
<main className="min-h-screen text-foreground bg-background">
<TopNav />
<Hero />
<TrustedBy />
<Features />
<CodeExample />
<Pricing />
<About />
<SiteFooter />
</main>
);
}
// (Removed unused SectionHeading component)
function Hero() {
return (
<section>
<div className="mx-auto max-w-6xl px-6 py-16 sm:py-24">
<h1 className="mt-6 text-center text-2xl sm:text-4xl font-semibold text-primary font-sans">
The open source email platform for everyone
</h1>
<p className="mt-4 text-center text-base sm:text-lg font-sans max-w-2xl mx-auto">
Send product, transactional and marketing emails.{" "}
<span className="text-primary font-normal">
Pay only for what you send
</span>{" "}
and not for storing contacts.
</p>
<div className="mt-8 flex flex-col sm:flex-row items-center justify-center gap-3">
<Button size="lg" className="px-6">
<a href={APP_URL} target="_blank" rel="noopener noreferrer">
Get started
</a>
</Button>
<GitHubStarsButton />
</div>
<p className="mt-3 text-center text-xs text-muted-foreground">
Open source Self-host in minutes Free tier
</p>
<div className=" mt-32 mx-auto max-w-5xl">
<div className="rounded-[18px] bg-primary/10 p-1 sm:p-1 ">
<div className="rounded-2xl bg-primary/20 p-1 sm:p-1 ">
<Image
src="/hero-light.webp"
alt="useSend product hero"
width={3456}
height={1914}
className="w-full h-auto rounded-xl block dark:hidden"
sizes="(min-width: 1024px) 900px, 100vw"
loading="eager"
priority={false}
/>
<Image
src="/hero-dark.webp"
alt="useSend product hero"
width={3456}
height={1914}
className="w-full h-auto rounded-xl hidden dark:block"
sizes="(min-width: 1024px) 900px, 100vw"
loading="eager"
priority={false}
/>
</div>
</div>
</div>
</div>
</section>
);
}
// TopNav moved to a dedicated client component in ~/components/TopNav
function TrustedBy() {
const featured = [
{
quote:
"Transitioned recently to open source email sender useSend for our 30k and growing newsletter. It's such a great product and amazing oss experience.",
author: "Marc Seitz",
company: "papermark.com",
image:
"https://pbs.twimg.com/profile_images/1176854646343852032/iYnUXJ-m_400x400.jpg",
},
{
quote:
"useSend was extremely easy to set up, and I love that it's open source. Koushik has been an absolute awesome person to deal with and helps us with any issues or feedback.",
author: "Tommerty",
company: "doras.to",
image:
"https://cdn.doras.to/doras/user/83bda65b-8d42-4011-9bf0-ab23402776f2-0.890688178917765.webp",
},
];
const quick = [
{
quote: "don't sleep on useSend",
author: "shellscape",
company: "jsx.email",
image:
"https://pbs.twimg.com/profile_images/1698447401781022720/b0DZSc_D_400x400.jpg",
},
{
quote: "Thank you for making useSend!",
author: "Andras Bacsai",
company: "coolify.io",
image:
"https://pbs.twimg.com/profile_images/1884210412524027905/jW4NB4rx_400x400.jpg",
},
{
quote: "I KNOW WHAT TO DO",
author: "VicVijayakumar",
company: "onetimefax.com",
image:
"https://pbs.twimg.com/profile_images/1665351804685524995/W4BpDx5Z_400x400.jpg",
},
];
return (
<section className="py-10 sm:py-20 ">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center tracking-wider text-muted-foreground">
<span className="">Builders and open source teams love </span>
<span className="text-primary font-bold">useSend</span>
</div>
{/* Top: 2 larger testimonials */}
<div className="mt-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
{featured.map((t) => (
<figure
key={t.author + t.company}
className="rounded-xl border border-primary/30 p-5 h-full"
>
<blockquote className="text-sm sm:text-base font-light font-sans ">
{t.quote}
</blockquote>
<div className="mt-5 flex items-center gap-3">
<Image
src={t.image}
alt={`${t.author} avatar`}
width={32}
height={32}
className=" rounded-md border-2 border-primary/50"
/>
<figcaption className="text-sm">
<span className="font-medium">{t.author}</span>
<a
href={`https://${t.company}`}
target="_blank"
className="text-muted-foreground hover:text-primary-light"
>
{" "}
{t.company}
</a>{" "}
</figcaption>
</div>
</figure>
))}
</div>
{/* Bottom: 3 multi-line testimonials (same style as top) */}
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-4">
{quick.map((t) => (
<figure
key={t.author + t.company}
className="rounded-xl border border-primary/30 p-5 h-full"
>
<blockquote className="text-sm sm:text-base font-light font-sans leading-relaxed">
{t.quote}
</blockquote>
<div className="mt-5 flex items-center gap-3">
<Image
src={t.image}
alt={`${t.author} avatar`}
width={32}
height={32}
className=" rounded-md border-2 border-primary/50"
/>
<figcaption className="text-sm">
<span className="font-medium">{t.author}</span>
<a
href={`https://${t.company}`}
target="_blank"
className="text-muted-foreground hover:text-primary-light"
>
{" "}
{t.company}
</a>
</figcaption>
</div>
</figure>
))}
</div>
</div>
</section>
);
}
function Features() {
// Top: 2 cards (with image area) — Analytics, Editor
const top = [
{
key: "feature-analytics",
title: "Analytics",
content:
"Track deliveries, opens, clicks, bounces and unsubscribes in real time with a simple, searchable log. Filter by domain, status, api key and export them. Track which campaigns perform best.",
imageLightSrc: "/emails-search-light.webp",
imageDarkSrc: "/emails-search-dark.webp",
},
{
key: "feature-editor",
title: "Marketing Email Editor",
content:
"Design beautiful campaigns without code using a visual, notion like WYSIWYG editor that works in major email clients. Reuse templates and brand styles, and personalize with variables.",
imageLightSrc: "/editor-light.webp",
imageDarkSrc: "/editor-dark.webp",
},
];
// Bottom: 3 cards (no images) — Contact Management, Suppression List, SMTP Relay Service
const bottom = [
{
key: "feature-contacts",
title: "Contact Management",
content:
"Manage contacts, lists, and consent in one place. Import and export easily, keep per-list subscription status. Contacts are automatically updated from bounces and complaints.",
},
{
key: "feature-suppression",
title: "Suppression List",
content:
"Prevent accidental sends. Automatically populated from bounces and complaints, and manage via import/export or API. Works with transactional and marketing emails.",
},
{
key: "feature-smtp",
title: "SMTP Relay",
content:
"Drop-in SMTP relay that works with any app or framework. Do not get vendor lock-in. Comes in handy with services like Supabase",
},
];
return (
<section id="features" className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
Features
</div>
</div>
{/* Top row: 2 side-by-side cards with images */}
<div className="mt-8 grid grid-cols-1 sm:grid-cols-2 gap-6">
{top.map((f) => (
<FeatureCard
key={f.key}
title={f.title}
content={f.content}
imageLightSrc={f.imageLightSrc}
imageDarkSrc={f.imageDarkSrc}
/>
))}
</div>
{/* Bottom row: 3 cards without images */}
<div className="mt-6 grid grid-cols-1 sm:grid-cols-3 gap-6">
{bottom.map((f) => (
<FeatureCardPlain key={f.key} title={f.title} content={f.content} />
))}
</div>
</div>
</section>
);
}
function CodeExample() {
const code = `import { UseSend } from "usesend-js";
const usesend = new UseSend("us_12345");
usesend.emails.send({
to: "hello@acme.com",
from: "hello@company.com",
subject: "useSend email",
html: "<p>useSend is the best open source product to send emails</p>",
text: "useSend is the best open source product to send emails",
});`;
return (
<section className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
Developers
</div>
<p className="mt-1 text-xs sm:text-sm text-muted-foreground max-w-2xl mx-auto">
Typed SDKs and simple APIs, so you can focus on product not
plumbing.
</p>
</div>
<div className="mt-8 overflow-hidden">
<div className=" py-2 text-xs text-muted-foreground">JavaScript</div>
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div className="bg-background rounded-xl overflow-hidden">
<CodeBlock
lang="javascript"
children={code}
className="p-4 rounded-[10px]"
/>
</div>
</div>
</div>
</div>
<div className="mt-6 flex items-center justify-center gap-3">
<Button size="lg" className="px-6">
<a
href="https://docs.usesend.com"
target="_blank"
rel="noopener noreferrer"
>
Read the docs
</a>
</Button>
</div>
</div>
</section>
);
}
function Pricing() {
const freePerks = [
"Send up to 3000 emails per month",
"Send up to 100 emails per day",
"Can have 1 contact book",
"Can have 1 domain",
"Can have 1 team member",
];
const paidPerks = [
"$10 monthly usage credits",
"Send transactional emails at $0.0004 per email",
"Send marketing emails at $0.001 per email",
"Can have unlimited contact books",
"Can have unlimited domains",
"Can have unlimited team members",
];
return (
<section id="pricing" className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
PRICING
</div>
<p className="mt-1 text-xs sm:text-sm text-muted-foreground max-w-2xl mx-auto">
pay for what you use, the most affordable email platform
</p>
</div>
<div className="mt-8 grid grid-cols-1 sm:grid-cols-2 gap-6">
<PricingCard
title="Free"
price="$0"
note="per month"
perks={freePerks}
/>
<PricingCard
title="Paid"
price="$10"
note="minimum usage per month"
perks={paidPerks}
/>
</div>
<div className="mt-8">
<PricingCalculator />
</div>
</div>
</section>
);
}
type PricingCardProps = {
title: string;
price: string;
note: string;
perks: string[];
};
function PricingCard({ title, price, note, perks }: PricingCardProps) {
return (
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="h-full rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div className="bg-background rounded-xl h-full flex flex-col p-5">
<h3 className=" font-medium">{title}</h3>
<div className="mt-2 text-4xl text-primary">{price}</div>
<div className="text-xs text-muted-foreground">{note}</div>
<ul className="mt-4 space-y-2 text-sm mb-20">
{perks.map((perk) => (
<li key={perk} className="flex items-start gap-2">
<CheckIcon className="w-4 h-4 mt-0.5 text-primary" />
<span>{perk}</span>
</li>
))}
</ul>
<div className="mt-auto pt-6">
<Button className="">
<a
href="https://app.usesend.com"
target="_blank"
rel="noopener noreferrer"
>
Get started
</a>
</Button>
</div>
</div>
</div>
</div>
);
}
function About() {
return (
<section id="about" className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
About
</div>
</div>
<div className="mt-8 max-w-3xl mx-auto text-sm sm:text-base space-y-4">
<p>
As most of email products out there, useSend also uses Amazon SES
under the hood to send emails. We provide an open and alternative
way to send emails reliably and cheaply with a great dashboard.
</p>
<p>
useSend is bootstrapped and funded by the cloud offering and
sponsors. If you self host useSend, please consider{" "}
<a
href="https://github.com/sponsors/KMKoushik"
target="_blank"
className="text-primary-light"
>
sponsoring us
</a>
.
</p>
</div>
</div>
</section>
);
}
// FAQ section removed per request
// Footer moved to ~/components/SiteFooter
// Minimal inline icons (stroke-based, sleek)
function CheckIcon({ className = "" }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
className={className}
aria-hidden="true"
>
<path d="M20 6 9 17l-5-5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}