@supernal/tts-blog
v1.1.1
Published
Automatic TTS integration for blog posts with frontmatter - powered by Supernal TTS
Maintainers
Readme
@supernal/tts-blog
Automatic Text-to-Speech for Blog Posts - Add audio narration to your blog with a single component.
Turn your blog posts into podcasts with just one line of code. Built on Supernal TTS, this package automatically converts your markdown/MDX blog posts into high-quality audio narration.
✨ Features
- 🎙️ Automatic Integration: Works with frontmatter-based blogs (Next.js, Gatsby, Docusaurus, etc.)
- 📝 Smart Content Extraction: Automatically cleans markdown and extracts readable text
- 🎨 Zero Config: Works out of the box with sensible defaults
- ⚙️ Highly Customizable: Override voices, providers, and styles via props or frontmatter
- 🚀 Production Ready: Built on the proven @supernal/tts-widget package
- ♿ Accessibility: Makes your content available to visually impaired readers
- 📱 Mobile Friendly: Perfect for readers on the go
🚀 Quick Start
Installation
npm install @supernal/tts-blog @supernal/tts-widgetBasic Usage
import { BlogTTSWidget } from '@supernal/tts-blog';
import '@supernal/tts-widget/widget.css';
function BlogPost({ post }) {
return (
<article>
<h1>{post.frontMatter.title}</h1>
{/* Add TTS with one line */}
<BlogTTSWidget
metadata={post.frontMatter}
content={post.content}
/>
<div>{post.content}</div>
</article>
);
}Environment Setup
Add your Supernal TTS API key to your environment:
# .env.local
NEXT_PUBLIC_TTS_API_URL=https://www.tts.supernal.ai
NEXT_PUBLIC_TTS_API_KEY=your_api_key_hereGet your free API key at tts.supernal.ai.
📖 Usage
Frontmatter Configuration
Control TTS behavior directly in your blog post frontmatter:
---
title: "My Awesome Blog Post"
author: "Jane Doe"
description: "A brief description of the post"
enableTTS: true # Enable/disable TTS (default: true)
ttsVoice: "fable" # Choose voice: fable, nova, onyx, coral, etc.
ttsProvider: "openai" # Choose provider: openai, cartesia, azure
---
Your blog content here...Component API
interface BlogTTSWidgetProps {
// Required
metadata: BlogMetadata; // Frontmatter from your blog post
content: string; // Raw markdown/MDX content
// Optional
position?: 'top' | 'bottom'; // Widget position (default: 'top')
style?: 'default' | 'minimal' | 'outline' | 'card';
className?: string;
voice?: string; // Override frontmatter voice
provider?: string; // Override frontmatter provider
apiUrl?: string; // Override API URL
apiKey?: string; // Override API key
renderPlaceholder?: (config) => ReactNode; // Custom rendering
}Advanced Examples
Custom Styling
<BlogTTSWidget
metadata={post.frontMatter}
content={post.content}
style="card"
className="my-custom-class"
/>Custom Placeholder Rendering
<BlogTTSWidget
metadata={post.frontMatter}
content={post.content}
renderPlaceholder={({ title, author, listeningTime }) => (
<div className="custom-tts-ui">
<h3>🎧 Listen to "{title}"</h3>
{author && <p>Read by AI voice</p>}
<span>{listeningTime} min audio</span>
</div>
)}
/>Programmatic Control
import { shouldEnableBlogTTS, getBlogTTSConfig } from '@supernal/tts-blog';
// Check if TTS should be enabled
const isEnabled = shouldEnableBlogTTS(post.frontMatter);
// Get TTS configuration
const config = getBlogTTSConfig(post.frontMatter);
// Returns: { enabled: boolean, voice: string, provider: string }🎨 Styling
The component uses the styles from @supernal/tts-widget. Import the CSS:
import '@supernal/tts-widget/widget.css';Or customize with your own styles:
.blog-tts-widget {
/* Your custom styles */
}🌐 Framework Integration
Next.js 13+ (App Router)
// app/blog/[slug]/page.tsx
import { BlogTTSWidget } from '@supernal/tts-blog';
import { getBlogPost } from '@/lib/blog';
export default async function BlogPost({ params }) {
const post = await getBlogPost(params.slug);
return (
<article>
<h1>{post.frontMatter.title}</h1>
<BlogTTSWidget
metadata={post.frontMatter}
content={post.content}
/>
<MDXRemote source={post.content} />
</article>
);
}Next.js (Pages Router)
// pages/blog/[slug].tsx
import { BlogTTSWidget } from '@supernal/tts-blog';
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.frontMatter.title}</h1>
<BlogTTSWidget
metadata={post.frontMatter}
content={post.content}
/>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}Gatsby
// src/templates/blog-post.js
import { BlogTTSWidget } from '@supernal/tts-blog';
export default function BlogPost({ data }) {
const post = data.markdownRemark;
return (
<article>
<h1>{post.frontmatter.title}</h1>
<BlogTTSWidget
metadata={post.frontmatter}
content={post.rawMarkdownBody}
/>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}Docusaurus
// src/theme/BlogPostPage/index.js
import { BlogTTSWidget } from '@supernal/tts-blog';
import OriginalBlogPostPage from '@theme-original/BlogPostPage';
export default function BlogPostPage(props) {
const { content: BlogPostContent } = props;
const { frontMatter, metadata } = BlogPostContent;
return (
<>
<BlogTTSWidget
metadata={frontMatter}
content={metadata.source}
/>
<OriginalBlogPostPage {...props} />
</>
);
}🔧 Utilities
Clean Markdown Content
import { cleanMarkdownContent } from '@supernal/tts-blog';
const plainText = cleanMarkdownContent(markdownContent);Extract Blog Text with Metadata
import { extractBlogText } from '@supernal/tts-blog';
const ttsText = extractBlogText(content, {
title: 'My Post',
author: 'John Doe',
description: 'A great article'
});
// Returns: "My Post. by John Doe. A great article. [content...]"Estimate Times
import { estimateReadingTime, estimateListeningTime } from '@supernal/tts-blog';
const readTime = estimateReadingTime(content); // minutes to read
const listenTime = estimateListeningTime(content); // minutes to listen🎙️ Voice Options
OpenAI Voices
alloy- Neutral, balancedecho- Warm, expressivefable- Professional narrator (recommended for blogs)onyx- Deep, authoritativenova- Friendly, conversationalshimmer- Clear, energeticcoral- Natural, engaging
Cartesia Voices
- Ultra-low latency for real-time applications
Azure Voices
- Cost-effective with extensive language support
🛠️ TypeScript Support
Full TypeScript support included:
import type { BlogMetadata, BlogTTSWidgetProps, BlogContent } from '@supernal/tts-blog';📚 Examples
Check out the examples directory for complete implementations:
- Next.js 13+ App Router
- Next.js Pages Router
- Gatsby
- Docusaurus
🤝 Related Packages
- @supernal/tts-widget - Core TTS widget
- @supernal/interface-nextjs - Full interface components
📄 License
MIT © Supernal Intelligence
