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

@contentgrowth/content-widget

v1.4.5

Published

Embed your Content Growth articles anywhere with beautiful, customizable components

Downloads

92

Readme

@content-growth/content-widget

Embed your Content Growth content anywhere with beautiful, customizable components.

Features

  • 🎨 Beautiful UI - Pre-styled components with light/dark themes
  • 🚀 Multi-Framework - React, Vue, Astro, and Vanilla JS support
  • 📦 Zero Config - Works out of the box with sensible defaults
  • 🎯 Type Safe - Full TypeScript support
  • Fast - Built-in caching and optimized performance
  • 🎨 Customizable - Multiple layouts and display modes
  • 📱 Responsive - Mobile-first design

Installation

npm install @content-growth/content-widget

Quick Start

Vanilla JavaScript

<!-- Add CSS -->
<link rel="stylesheet" href="node_modules/@content-growth/content-widget/dist/styles.css">

<!-- Add container -->
<div id="blog"></div>

<!-- Initialize widget -->
<script type="module">
  import { ContentGrowthWidget } from '@content-growth/content-widget/widget';
  
  new ContentGrowthWidget(document.getElementById('blog'), {
    apiKey: 'pk_your_key_here',
    layout: 'cards'
  });
</script>

React

import { ContentList } from '@content-growth/content-widget/react';
import '@content-growth/content-widget/styles.css';

function App() {
  return <ContentList apiKey="pk_your_key_here" layout="cards" />;
}

With Hooks:

import { useArticles } from '@content-growth/content-widget/react';

function CustomContentList() {
  const { articles, loading, error } = useArticles({
    apiKey: 'pk_your_key_here',
    page: 1,
    limit: 12
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {articles.map(article => (
        <div key={article.uuid}>
          <h2>{article.title}</h2>
          <p>{article.summary}</p>
        </div>
      ))}
    </div>
  );
}

Vue

<template>
  <ContentList api-key="pk_your_key_here" layout="cards" />
</template>

<script setup>
import { ContentList } from '@content-growth/content-widget/vue';
import '@content-growth/content-widget/styles.css';
</script>

With Composables:

<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">Error: {{ error.message }}</div>
    <div v-else>
      <div v-for="article in articles" :key="article.uuid">
        <h2>{{ article.title }}</h2>
        <p>{{ article.summary }}</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { useArticles } from '@content-growth/content-widget/vue';

const { articles, loading, error } = useArticles({
  apiKey: 'pk_your_key_here',
  page: 1,
  limit: 12
});
</script>

Astro

---
import { ContentList } from '@content-growth/content-widget/astro';
import '@content-growth/content-widget/styles.css';
---

<ContentList apiKey="pk_your_key_here" />

API Client (Framework Agnostic)

import { ContentGrowthClient } from '@content-growth/content-widget/core';

const client = new ContentGrowthClient({
  apiKey: 'pk_your_key_here'
});

const { articles, pagination } = await client.listArticles({
  page: 1,
  limit: 12,
  tags: ['tutorial']
});

Components

Vanilla JS Widget

import { ContentGrowthWidget } from '@content-growth/content-widget/widget';

const widget = new ContentGrowthWidget(container, {
  apiKey: 'pk_your_key_here',
  baseUrl: 'https://api.content-growth.com',
  layoutMode: 'cards', // 'cards' | 'rows'
  displayMode: 'comfortable', // 'compact' | 'comfortable' | 'spacious'
  theme: 'light', // 'light' | 'dark'
  pageSize: 12,
  tags: ['tutorial', 'guide'],
  category: 'guides',
  viewerMode: 'inline', // 'inline' | 'modal' | 'external'
  mode: 'list' // 'list' | 'article-only'
});

React Components

ContentList:

<ContentList
  apiKey="pk_your_key_here"
  layout="cards"
  displayMode="comfortable"
  theme="light"
  pageSize={12}
  tags={['tutorial', 'guide']}
  category="guides"
  showPagination={true}
/>

ContentViewer:

<ContentViewer
  apiKey="pk_your_key_here"
  uuid="article-uuid"
  theme="light"
  showBackButton={true}
  backUrl="/articles"
/>

Hooks:

  • useArticles(options) - Fetch articles list
  • useArticle(options) - Fetch single article
  • useCategories(options) - Fetch categories
  • useTags(options) - Fetch tags

Vue Components

ContentList:

<ContentList
  api-key="pk_your_key_here"
  layout="cards"
  display-mode="comfortable"
  theme="light"
  :page-size="12"
  :tags="['tutorial', 'guide']"
  category="guides"
  :show-pagination="true"
/>

ContentViewer:

<ContentViewer
  api-key="pk_your_key_here"
  uuid="article-uuid"
  theme="light"
  :show-back-button="true"
  back-url="/articles"
/>

Composables:

  • useArticles(options) - Fetch articles list
  • useArticle(options) - Fetch single article
  • useCategories(options) - Fetch categories
  • useTags(options) - Fetch tags

Astro Components

ContentList:

<ContentList
  apiKey="pk_your_key_here"
  layout="cards"
  displayMode="comfortable"
  theme="light"
  pageSize={12}
  tags={['tutorial', 'guide']}
  category="guides"
  showPagination={true}
/>

ContentViewer:

<ContentViewer
  apiKey="pk_your_key_here"
  uuid={uuid}
  theme="light"
  showBackButton={true}
  backUrl="/articles"
/>

ContentCard:

A standalone article card component. Use it to display individual articles outside of ContentList.

<!-- Load by slug -->
<ContentCard
  apiKey="pk_your_key_here"
  slug="my-article-slug"
  linkPattern="/articles/{slug}"
/>

<!-- Load by UUID -->
<ContentCard
  apiKey="pk_your_key_here"
  uuid="article-uuid"
/>

<!-- Use pre-loaded article data -->
<ContentCard
  article={articleData}
  linkPattern="/articles/{slug}"
  showSummary={true}
  showTags={true}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | apiKey | string | - | Your API key (required unless article is provided) | | article | Article | - | Pre-loaded article data (skips API fetch) | | slug | string | - | Load specific article by slug | | uuid | string | - | Load specific article by UUID | | linkPattern | string | '/articles/{slug}' | URL pattern for link | | linkTarget | string | - | Link target attribute | | showSummary | boolean | true | Show article summary | | summaryMaxLength | number | - | Truncate summary at length | | showTags | boolean | false | Show article tags | | showCategory | boolean | true | Show category badge |

FeaturedCard:

A compact card displaying the Featured Summary with customizable styling. Perfect for landing pages.

<!-- Find latest article in category -->
<FeaturedCard
  apiKey="pk_your_key_here"
  category="announce"
  linkPattern="/articles/{slug}"
  layout="horizontal"
  borderStyle="dashed"
  borderColor="#e5e7eb"
  padding="20px"
  ctaText="Read full story"
/>

<!-- Load specific article by slug -->
<FeaturedCard
  apiKey="pk_your_key_here"
  slug="my-article-slug"
  linkPattern="/articles/{slug}"
/>

<!-- Use pre-loaded article data (for lists) -->
<FeaturedCard
  article={articleData}
  linkPattern="/articles/{slug}"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | apiKey | string | - | Your API key (required unless article is provided) | | article | Article | - | Pre-loaded article data (skips API fetch) | | slug | string | - | Load specific article by slug | | uuid | string | - | Load specific article by UUID | | category | string | - | Filter by category | | tags | string[] | [] | Filter by tags | | layout | 'vertical' | 'horizontal' | auto | Card layout (auto uses article setting) | | borderStyle | 'none' | 'line' | 'dashed' | 'none' | Card border style | | borderColor | string | '#e5e7eb' | Border color (CSS value) | | cardBackground | string | 'none' | Card background ('none' = transparent) | | itemsBackground | string | '#f3f4f6' | Background for list/quote section | | padding | string | - | Custom padding (e.g., '10px', '2rem 3rem') | | ctaText | string | 'Read full story' | Call-to-action text | | showAuthor | boolean | false | Show author name | | showReadingTime | boolean | false | Show reading time | | linkPattern | string | '/articles/{slug}' | URL pattern for link |

Featured Summary Types:

FeaturedCard supports structured JSON summaries generated via the portal wizard:

| Type | Description | |------|-------------| | list | Intro text on left, bulleted key points on right | | steps | Intro text on left, numbered action steps on right | | quote | Intro text on left, styled pullquote on right | | classic | Simple text summary (legacy) |

Featured Cards List:

Display all articles as FeaturedCards in a grid using displayAs:

<ContentList
  apiKey="pk_your_key_here"
  displayAs="featured-cards"
  linkPattern="/articles/{slug}"
  pageSize={12}
/>

| displayAs Value | Description | |-----------------|-------------| | 'default' | Standard card/row layout | | 'featured-cards' | Renders each article as a FeaturedCard |

API Client

ContentGrowthClient

import { ContentGrowthClient } from '@content-growth/content-widget/core';

const client = new ContentGrowthClient({
  apiKey: 'pk_your_key_here',
  baseUrl: 'https://api.content-growth.com', // optional
  cacheTTL: 300000, // 5 minutes, optional
  debug: false // optional
});

Methods

listArticles(options?)

const { articles, pagination } = await client.listArticles({
  page: 1,
  limit: 12,
  tags: ['tutorial', 'guide'],
  category: 'guides'
});

getArticle(uuid)

const article = await client.getArticle('article-uuid');

getCategories()

const { categories } = await client.getCategories();

getTags()

const { tags } = await client.getTags();

clearCache()

client.clearCache();

Styling

Import the CSS file:

import '@content-growth/content-widget/styles.css';

Customization

Override CSS variables:

.cg-content-list {
  --cg-primary: #3b82f6;
  --cg-primary-hover: #2563eb;
  --cg-bg: #ffffff;
  --cg-bg-secondary: #f9fafb;
  --cg-text: #1f2937;
  --cg-text-secondary: #6b7280;
  --cg-border: #e5e7eb;
  --cg-radius: 12px;
}

TypeScript

Full TypeScript support with exported types:

import type {
  Article,
  ArticleWithContent,
  Pagination,
  Category,
  Tag,
  ClientConfig,
  ListArticlesOptions,
  ContentListProps,
  ContentViewerProps
} from '@content-growth/content-widget/core';

Getting Your API Key

  1. Sign in to Content Growth
  2. Go to Settings → API Keys
  3. Create a new API key
  4. Copy the key (starts with pk_)

Examples

Next.js App Router

// app/articles/page.tsx
import { ContentList } from '@content-growth/content-widget/react';
import '@content-growth/content-widget/styles.css';

export default function ArticlesPage() {
  return (
    <main>
      <h1>Articles</h1>
      <ContentList apiKey={process.env.PUBLIC_CG_API_KEY!} />
    </main>
  );
}

Nuxt 3

<!-- pages/articles.vue -->
<template>
  <div>
    <h1>Articles</h1>
    <ContentList :api-key="apiKey" />
  </div>
</template>

<script setup>
import { ContentList } from '@content-growth/content-widget/vue';
import '@content-growth/content-widget/styles.css';

const config = useRuntimeConfig();
const apiKey = config.public.cgApiKey;
</script>

Vanilla HTML

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@content-growth/content-widget/dist/styles.css">
</head>
<body>
  <div id="blog"></div>
  
  <script type="module">
    import { ContentGrowthWidget } from 'https://unpkg.com/@content-growth/content-widget/dist/widget/index.js';
    
    new ContentGrowthWidget(document.getElementById('blog'), {
      apiKey: 'pk_your_key_here'
    });
  </script>
</body>
</html>

License

MIT

Support