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

@oxmo88/next-wordpress-utils

v1.0.0

Published

Comprehensive WordPress/Next.js utilities: SEO, sitemaps, video schemas, URL handling, text processing, and multi-locale support

Downloads

50

Readme

@next-wordpress/utils

Comprehensive WordPress/Next.js utilities for SEO, sitemaps, video schemas, URL handling, text processing, and multi-locale support.

Features

  • 🎯 Configuration-Based: All utilities accept a site configuration, making them fully reusable across projects
  • 🌍 Multi-Locale Support: Built-in support for multiple languages and locales
  • 📱 SEO Optimized: Generate canonical URLs, hreflang tags, OpenGraph metadata, and structured data
  • 🎬 Video Schema: Automatic video detection and VideoObject structured data generation
  • 📄 Sitemap Utilities: Priority calculation, URL validation, and sitemap splitting
  • 🔤 Text Processing: HTML stripping, slug generation, entity decoding, and more
  • 📦 Tree-Shakeable: Import only what you need with modular exports
  • 🔷 TypeScript: Full type safety with TypeScript definitions
  • 📚 Dual Format: Works with both ESM and CommonJS

Installation

pnpm add @next-wordpress/utils

Quick Start

1. Create Your Site Configuration

First, create a configuration file for your site:

// lib/site.config.ts
import { SiteConfig } from '@next-wordpress/utils'

export const siteConfig: SiteConfig = {
  baseUrl: 'https://yourdomain.com',
  siteName: 'Your Site Name',
  defaultLocale: 'en-US',
  locales: {
    'en-US': '',        // Default locale, no prefix
    'es-ES': 'es',      // Spanish: /es/*
    'fr-FR': 'fr',      // French: /fr/*
  },
  pathMappings: {
    article: {
      'en-US': 'news',
      'es-ES': 'noticias',
      'fr-FR': 'actualites',
    },
    page: {
      'en-US': 'pages',
      'es-ES': 'paginas',
      'fr-FR': 'pages',
    },
  },
  metadata: {
    title: 'Your Site Title',
    description: 'Your site description',
    keywords: ['keyword1', 'keyword2'],
    author: 'Your Name',
    twitter: {
      site: '@yourhandle',
      creator: '@yourhandle',
    },
    openGraph: {
      type: 'website',
      locale: 'en_US',
    },
  },
}

2. Use the Utilities

import { getCanonicalUrl, generatePageMetadata } from '@next-wordpress/utils'
import { siteConfig } from '@/lib/site.config'

// Generate canonical URL
const canonicalUrl = getCanonicalUrl('/about', siteConfig)
// Result: "https://yourdomain.com/about"

// Generate page metadata
const metadata = generatePageMetadata(
  {
    title: 'About Us',
    description: 'Learn about our company',
    type: 'website',
  },
  '/about',
  siteConfig
)

Modular Imports

Import only what you need to reduce bundle size:

// Import everything
import * as utils from '@next-wordpress/utils'

// Import specific modules
import { generateSeoSlug, stripHtml } from '@next-wordpress/utils/text'
import { getCanonicalUrl } from '@next-wordpress/utils/url'
import { calculateContentPriority } from '@next-wordpress/utils/sitemap'
import { extractVideosFromContent } from '@next-wordpress/utils/video'

// Import SEO convenience module (includes text, url, sitemap utilities)
import {
  generateSeoSlug,
  getCanonicalUrl,
  calculateContentPriority
} from '@next-wordpress/utils/seo'

API Reference

Configuration Types

SiteConfig

Main configuration interface for all utilities.

interface SiteConfig {
  baseUrl: string                                    // e.g., "https://yourdomain.com"
  siteName: string                                   // Site name for Open Graph
  defaultLocale: string                              // e.g., "en-US"
  locales: Record<string, string>                    // { "en-US": "", "es-ES": "es" }
  pathMappings?: Record<string, Record<string, string>> // Content type paths per locale
  metadata?: {
    title?: string
    description?: string
    keywords?: string[]
    author?: string
    twitter?: { site?: string; creator?: string }
    openGraph?: { type?: string; locale?: string }
  }
}

MetadataTemplate

Template for page metadata generation.

interface MetadataTemplate {
  title: string
  description: string
  keywords?: string[]
  author?: string
  type?: 'website' | 'article' | 'product'
  image?: string
  publishedTime?: string
  modifiedTime?: string
  section?: string
  tags?: string[]
}

URL Utilities

getCanonicalUrl()

Generate canonical URL from path and config.

function getCanonicalUrl(path: string = '', config: SiteConfig): string

// Example
const url = getCanonicalUrl('/about', siteConfig)
// Result: "https://yourdomain.com/about"

getLanguageAlternates()

Generate language alternate URLs for hreflang tags.

function getLanguageAlternates(
  basePath: string = '',
  config: SiteConfig
): Record<string, string>

// Example
const alternates = getLanguageAlternates('/about', siteConfig)
// Result: {
//   'en-US': 'https://yourdomain.com/about',
//   'es-ES': 'https://yourdomain.com/es/about',
//   'fr-FR': 'https://yourdomain.com/fr/about',
//   'x-default': 'https://yourdomain.com/about'
// }

generatePageMetadata()

Generate complete page metadata using config and template.

function generatePageMetadata(
  template: MetadataTemplate,
  path: string = '',
  config: SiteConfig
)

// Example
const metadata = generatePageMetadata(
  {
    title: 'About Us',
    description: 'Learn about our company',
    type: 'website',
    image: 'https://yourdomain.com/og-image.jpg',
  },
  '/about',
  siteConfig
)

// Returns Next.js metadata object with:
// - title, description, keywords
// - metadataBase
// - alternates (canonical + hreflang)
// - openGraph
// - twitter card

getArticleUrl()

Get article URL by slug and locale using config.

function getArticleUrl(
  slug: string,
  locale: string,
  contentType: string,
  config: SiteConfig
): string

// Example
const url = getArticleUrl('hello-world-123', 'es-ES', 'article', siteConfig)
// Result: "/es/noticias/hello-world-123"

getArticleCanonicalUrl()

Build full canonical URL for article.

function getArticleCanonicalUrl(
  slug: string,
  locale: string,
  contentType: string,
  config: SiteConfig
): string

// Example
const url = getArticleCanonicalUrl('hello-world-123', 'es-ES', 'article', siteConfig)
// Result: "https://yourdomain.com/es/noticias/hello-world-123"

parseLocaleFromPathname()

Parse locale from pathname.

function parseLocaleFromPathname(
  pathname: string,
  config: SiteConfig
): string

// Example
const locale = parseLocaleFromPathname('/es/noticias/article', siteConfig)
// Result: "es-ES"

getHomeUrl()

Get home page URL for locale.

function getHomeUrl(locale: string, config: SiteConfig): string

// Example
const homeUrl = getHomeUrl('es-ES', siteConfig)
// Result: "/es"

buildLocalizedPath()

Build localized path.

function buildLocalizedPath(
  path: string,
  locale: string,
  config: SiteConfig
): string

// Example
const path = buildLocalizedPath('/about', 'es-ES', siteConfig)
// Result: "/es/about"

normalizeUrl()

Normalize URL to match config base URL.

function normalizeUrl(url: string, config: SiteConfig): string

// Example
const normalized = normalizeUrl('http://olddomain.com/page', siteConfig)
// Result: "https://yourdomain.com/page"

cn()

Tailwind CSS class merger utility (combines clsx + tailwind-merge).

function cn(...inputs: ClassValue[]): string

// Example
const className = cn('px-4', 'py-2', { 'bg-blue-500': isActive })

Text Utilities

generateSeoSlug()

Generate SEO-friendly slug from title.

function generateSeoSlug(title: string, id: number): string

// Example
const slug = generateSeoSlug('Héllo World! 🎉', 123)
// Result: "hello-world-123"

extractIdFromSlug()

Extract ID from slug.

function extractIdFromSlug(slug: string): number | null

// Example
const id = extractIdFromSlug('hello-world-123')
// Result: 123

stripHtml()

Strip HTML tags and decode HTML entities.

function stripHtml(html: string): string

// Example
const text = stripHtml('<p>Hello &amp; welcome!</p>')
// Result: "Hello & welcome!"

truncateText()

Truncate text to specified length with ellipsis.

function truncateText(
  text: string,
  maxLength: number,
  suffix: string = '...'
): string

// Example
const truncated = truncateText('This is a long text', 10)
// Result: "This is a..."

extractTextFromHtml()

Extract plain text from HTML.

function extractTextFromHtml(html: string): string

// Example
const text = extractTextFromHtml('<p>Hello</p><p>World</p>')
// Result: "Hello World"

normalizeWhitespace()

Normalize whitespace in text.

function normalizeWhitespace(text: string): string

// Example
const normalized = normalizeWhitespace('Hello    \n  World')
// Result: "Hello World"

toTitleCase()

Convert text to title case.

function toTitleCase(text: string): string

// Example
const title = toTitleCase('hello world')
// Result: "Hello World"

generateExcerpt()

Generate excerpt from text.

function generateExcerpt(
  text: string,
  maxLength: number = 160,
  suffix: string = '...'
): string

// Example
const excerpt = generateExcerpt(longText, 100)

Sitemap Utilities

calculateContentPriority()

Calculate dynamic priority and changefreq based on content age.

function calculateContentPriority(modifiedDate: string | Date): {
  priority: number
  changefreq: ChangeFreq
}

// Example
const { priority, changefreq } = calculateContentPriority('2024-01-15')
// Recent content: { priority: 0.9, changefreq: 'daily' }
// Older content: { priority: 0.5, changefreq: 'monthly' }

generateSitemapUrl()

Generate a sitemap URL entry.

interface SitemapRoute {
  url: string
  lastModified?: string | Date
  changeFrequency?: ChangeFreq
  priority?: number
  alternates?: { languages?: Record<string, string> }
}

function generateSitemapUrl(
  path: string,
  config: SiteConfig,
  modifiedDate?: string | Date,
  overrides?: Partial<SitemapRoute>
): SitemapRoute

// Example
const sitemapEntry = generateSitemapUrl('/about', siteConfig, '2024-01-15')

sortSitemapUrls()

Sort sitemap URLs by priority (high to low) and lastModified (recent to old).

function sortSitemapUrls(urls: SitemapRoute[]): SitemapRoute[]

validateSitemapUrl()

Validate a sitemap URL entry.

function validateSitemapUrl(route: SitemapRoute): boolean

splitSitemapUrls()

Split sitemap into chunks (Google recommends max 50,000 URLs).

function splitSitemapUrls<T>(urls: T[], chunkSize: number = 50000): T[][]

// Example
const chunks = splitSitemapUrls(urls, 50000)

Video Utilities

VideoData Interface

interface VideoData {
  type: 'youtube' | 'youtube-short' | 'tiktok' | 'instagram-reel' | 'facebook-reel' | 'vimeo' | 'mp4' | 'other'
  embedUrl?: string
  contentUrl?: string
  videoId?: string
  thumbnailUrl?: string
  duration?: string      // ISO 8601 format (e.g., "PT3M10S")
  uploadDate?: string
  name?: string
  description?: string
  isShortForm?: boolean  // True for short-form content (under 60s)
}

extractVideosFromContent()

Extract all videos from HTML content. Supports:

  • YouTube (regular, shorts, youtu.be)
  • TikTok
  • Instagram Reels
  • Facebook Reels
  • Vimeo
  • Self-hosted MP4
function extractVideosFromContent(htmlContent: string): VideoData[]

// Example
const videos = extractVideosFromContent(articleContent)
// Result: [
//   {
//     type: 'youtube',
//     videoId: 'dQw4w9WgXcQ',
//     embedUrl: 'https://www.youtube.com/embed/dQw4w9WgXcQ',
//     contentUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
//     thumbnailUrl: 'https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg'
//   }
// ]

extractDurationFromText()

Extract video duration from article text. Looks for patterns like:

  • "3 minutes 10 seconds"
  • "3:10"
  • "3m10s"
function extractDurationFromText(text: string): string | undefined

// Example
const duration = extractDurationFromText('Watch this 3:45 video')
// Result: "PT3M45S" (ISO 8601 format)

generateVideoSchema()

Generate VideoObject structured data for a single video.

function generateVideoSchema(
  video: VideoData,
  article: {
    title: string
    excerpt: string
    date: string
    modified: string
    slug: string
    featuredImage: string
  },
  baseUrl: string,
  siteName: string = 'Site'
): any

// Returns Schema.org VideoObject

generateVideoSchemas()

Main function: Detect videos and generate all VideoObject schemas.

function generateVideoSchemas(
  htmlContent: string,
  article: {
    title: string
    excerpt: string
    content: string
    date: string
    modified: string
    slug: string
    featuredImage: string
  },
  baseUrl: string,
  siteName: string = 'Site'
): any[]

// Example
const videoSchemas = generateVideoSchemas(
  article.content,
  article,
  siteConfig.baseUrl,
  siteConfig.siteName
)

// Add to page metadata
export async function generateMetadata() {
  const videoSchemas = generateVideoSchemas(...)

  return {
    // ... other metadata
    other: {
      'application/ld+json': JSON.stringify(videoSchemas)
    }
  }
}

hasVideos()

Check if content contains any videos.

function hasVideos(htmlContent: string): boolean

// Example
if (hasVideos(article.content)) {
  const schemas = generateVideoSchemas(...)
}

normalizeDateWithTimezone()

Normalize date to ISO 8601 format with timezone.

function normalizeDateWithTimezone(dateString: string): string

Usage Examples

Example 1: Article Page with Full SEO

// app/[locale]/news/[slug]/page.tsx
import {
  generatePageMetadata,
  getArticleCanonicalUrl,
  getLanguageAlternates,
  generateVideoSchemas
} from '@next-wordpress/utils'
import { siteConfig } from '@/lib/site.config'

export async function generateMetadata({ params }) {
  const article = await fetchArticle(params.slug)

  // Generate video schemas if article contains videos
  const videoSchemas = generateVideoSchemas(
    article.content,
    article,
    siteConfig.baseUrl,
    siteConfig.siteName
  )

  return generatePageMetadata(
    {
      title: article.title,
      description: article.excerpt,
      type: 'article',
      image: article.featuredImage,
      publishedTime: article.date,
      modifiedTime: article.modified,
      section: article.category,
      tags: article.tags,
    },
    getArticleUrl(params.slug, params.locale, 'article', siteConfig),
    siteConfig
  )
}

Example 2: Dynamic Sitemap

// app/sitemap.ts
import { MetadataRoute } from 'next'
import {
  generateSitemapUrl,
  sortSitemapUrls,
  splitSitemapUrls
} from '@next-wordpress/utils'
import { siteConfig } from '@/lib/site.config'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const articles = await fetchAllArticles()

  const articleUrls = articles.map(article =>
    generateSitemapUrl(
      `/news/${article.slug}`,
      siteConfig,
      article.modified
    )
  )

  const staticUrls = [
    generateSitemapUrl('/', siteConfig),
    generateSitemapUrl('/about', siteConfig),
    generateSitemapUrl('/contact', siteConfig),
  ]

  const allUrls = [...staticUrls, ...articleUrls]
  const sortedUrls = sortSitemapUrls(allUrls)

  return sortedUrls
}

Example 3: Article Card with SEO Slug

// components/article-card.tsx
import { generateSeoSlug, truncateText, stripHtml } from '@next-wordpress/utils/text'
import { getArticleUrl } from '@next-wordpress/utils/url'
import { siteConfig } from '@/lib/site.config'

export function ArticleCard({ article, locale }) {
  const slug = generateSeoSlug(article.title, article.id)
  const url = getArticleUrl(slug, locale, 'article', siteConfig)
  const excerpt = truncateText(stripHtml(article.excerpt), 150)

  return (
    <a href={url}>
      <h3>{article.title}</h3>
      <p>{excerpt}</p>
    </a>
  )
}

Example 4: Multi-Locale Breadcrumbs

// components/breadcrumb.tsx
import { buildLocalizedPath } from '@next-wordpress/utils/url'
import { siteConfig } from '@/lib/site.config'

export function Breadcrumb({ items, locale }) {
  return (
    <nav>
      {items.map((item, index) => {
        const path = buildLocalizedPath(item.href, locale, siteConfig)
        return (
          <a key={index} href={path}>
            {item.label}
          </a>
        )
      })}
    </nav>
  )
}

TypeScript Support

All functions and types are fully typed. Your IDE will provide autocomplete and type checking:

import { SiteConfig, MetadataTemplate } from '@next-wordpress/utils'

// Type-safe configuration
const config: SiteConfig = {
  baseUrl: 'https://yourdomain.com',
  // ... TypeScript will validate all properties
}

// Type-safe template
const template: MetadataTemplate = {
  title: 'Page Title',
  description: 'Page description',
  type: 'article', // Only accepts: 'website' | 'article' | 'product'
}

License

MIT

Contributing

Contributions are welcome! Please ensure all tests pass and add tests for new features.

Support

For issues and feature requests, please use the GitHub issue tracker.