npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@supernal/tts-blog

v1.1.1

Published

Automatic TTS integration for blog posts with frontmatter - powered by Supernal TTS

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-widget

Basic 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_here

Get 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, balanced
  • echo - Warm, expressive
  • fable - Professional narrator (recommended for blogs)
  • onyx - Deep, authoritative
  • nova - Friendly, conversational
  • shimmer - Clear, energetic
  • coral - 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

📄 License

MIT © Supernal Intelligence

🔗 Links