Examples
Complete example implementations for different frameworks and use cases.
Next.js Blog
A complete blog implementation with ISR, sitemap, and SEO.
// Project structure
├── app/
│ ├── blog/
│ │ ├── page.tsx # Blog listing
│ │ └── [slug]/
│ │ └── page.tsx # Article page
│ ├── sitemap.ts # Sitemap
│ └── layout.tsx
├── lib/
│ └── trident.ts # Client config
└── styles/
└── trident.css # Component styleslib/trident.ts
import { createTridentClient } from "@trident/client";
export const trident = createTridentClient({
apiKey: process.env.TRIDENT_API_KEY!,
});app/blog/page.tsx
import { trident } from "@/lib/trident";
import Link from "next/link";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Blog | My Company",
description: "Latest articles and insights",
};
export default async function BlogPage() {
const { data: articles } = await trident.getArticles({
limit: 20,
});
return (
<main className="mx-auto max-w-4xl px-4 py-16">
<h1 className="text-4xl font-bold">Blog</h1>
<div className="mt-12 divide-y">
{articles.map((article) => (
<article key={article.id} className="py-8">
<Link href={`/blog/${article.slug}`}>
<h2 className="text-2xl font-semibold hover:text-blue-600">
{article.title}
</h2>
</Link>
<p className="mt-3 text-gray-600">{article.excerpt}</p>
<div className="mt-4 flex items-center gap-4 text-sm text-gray-500">
<time>
{new Date(article.publishedAt!).toLocaleDateString()}
</time>
{article.wordCount && (
<span>{Math.ceil(article.wordCount / 200)} min read</span>
)}
</div>
</article>
))}
</div>
</main>
);
}
export const revalidate = 3600;app/blog/[slug]/page.tsx
import { trident } from "@/lib/trident";
import { ArticleRenderer, StructuredData, FAQSchema } from "@trident/client/components";
import { generateArticleMetadata } from "@trident/client/nextjs";
import { notFound } from "next/navigation";
interface Props {
params: { slug: string };
}
export async function generateMetadata({ params }: Props) {
try {
const article = await trident.getArticle(params.slug);
return generateArticleMetadata(article, {
baseUrl: "https://mycompany.com",
});
} catch {
return { title: "Article Not Found" };
}
}
export async function generateStaticParams() {
const entries = await trident.getSitemap();
return entries.map((entry) => ({ slug: entry.slug }));
}
export default async function ArticlePage({ params }: Props) {
try {
const article = await trident.getArticle(params.slug);
return (
<>
<StructuredData
article={article}
baseUrl="https://mycompany.com"
organization={{
name: "My Company",
logo: "https://mycompany.com/logo.png",
}}
/>
{article.content?.faq && <FAQSchema questions={article.content.faq} />}
<article className="mx-auto max-w-3xl px-4 py-16">
<header className="mb-12">
<h1 className="text-4xl font-bold">{article.title}</h1>
<div className="mt-4 flex items-center gap-4 text-gray-500">
<time>{new Date(article.publishedAt!).toLocaleDateString()}</time>
{article.wordCount && (
<span>{Math.ceil(article.wordCount / 200)} min read</span>
)}
</div>
</header>
{article.content && (
<ArticleRenderer article={article} className="prose prose-lg" />
)}
</article>
</>
);
} catch {
notFound();
}
}
export const revalidate = 3600;app/sitemap.ts
import { trident } from "@/lib/trident";
import { generateSitemapEntries } from "@trident/client/nextjs";
export default async function sitemap() {
const entries = await trident.getSitemap();
const articleEntries = generateSitemapEntries(entries, {
baseUrl: "https://mycompany.com",
urlPattern: "/blog/:slug",
});
return [
{ url: "https://mycompany.com", lastModified: new Date() },
{ url: "https://mycompany.com/about", lastModified: new Date() },
...articleEntries,
];
}
export const revalidate = 3600;styles/trident.css
/* Trident component styles */
[data-trident-paragraph] {
@apply mb-6 leading-relaxed text-gray-700;
}
[data-trident-heading="2"] {
@apply mb-4 mt-10 text-2xl font-bold text-gray-900;
}
[data-trident-heading="3"] {
@apply mb-3 mt-8 text-xl font-semibold text-gray-900;
}
[data-trident-list] {
@apply my-6 space-y-2 pl-6;
}
[data-trident-list="ordered"] {
@apply list-decimal;
}
[data-trident-list="unordered"] {
@apply list-disc;
}
[data-trident-callout] {
@apply my-6 rounded-lg border-l-4 p-4;
}
[data-trident-callout="tip"] {
@apply border-green-500 bg-green-50;
}
[data-trident-callout="warning"] {
@apply border-yellow-500 bg-yellow-50;
}
[data-trident-callout="note"] {
@apply border-blue-500 bg-blue-50;
}
[data-trident-table] {
@apply my-6 w-full border-collapse text-left;
}
[data-trident-th],
[data-trident-td] {
@apply border border-gray-300 px-4 py-2;
}
[data-trident-th] {
@apply bg-gray-100 font-semibold;
}
[data-trident-code] {
@apply my-6 overflow-x-auto rounded-lg bg-gray-900 p-4 text-sm text-gray-100;
}
[data-trident-image] {
@apply my-6;
}
[data-trident-image] img {
@apply rounded-lg;
}
[data-trident-caption] {
@apply mt-2 text-center text-sm text-gray-500;
}
[data-trident-quote] {
@apply my-6 border-l-4 border-gray-300 pl-4 italic text-gray-600;
}
[data-trident-faq] {
@apply my-8 space-y-6;
}
[data-trident-faq-item] {
@apply rounded-lg border border-gray-200 p-6;
}
[data-trident-faq-question] {
@apply text-lg font-semibold text-gray-900;
}
[data-trident-faq-answer] {
@apply mt-3 text-gray-600;
}
[data-trident-sources] {
@apply mt-12 border-t pt-8;
}
[data-trident-cta] {
@apply my-12 rounded-xl bg-blue-50 p-8 text-center;
}Framework-Agnostic Usage
Use the core client without React components:
import { createTridentClient } from "@trident/client";
import {
contentToHtml,
contentToText,
calculateReadingTime,
} from "@trident/client/generic";
const client = createTridentClient({
apiKey: process.env.TRIDENT_API_KEY!,
});
// Fetch article
const article = await client.getArticle("my-article");
// Convert to HTML (for SSR without React)
const html = contentToHtml(article.content);
// Convert to plain text (for search indexing)
const text = contentToText(article.content);
// Calculate reading time
const minutes = calculateReadingTime(article.content);Multi-Product Setup
Handle multiple products with dynamic routes:
// app/[product]/blog/[slug]/page.tsx
interface Props {
params: { product: string; slug: string };
}
export async function generateStaticParams() {
const entries = await trident.getSitemap();
return entries.map((entry) => ({
product: entry.productSlug,
slug: entry.slug,
}));
}
export default async function ArticlePage({ params }: Props) {
const article = await trident.getArticle(params.slug, {
product: params.product,
});
// ...
}More Examples
Check out the examples directory on GitHub for complete working examples including:
- • Next.js App Router with Tailwind CSS
- • Next.js Pages Router
- • Remix
- • Astro
- • Multi-territory/multi-language setup