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 🙏

© 2025 – Pkg Stats / Ryan Hefner

astro-loader-goodreads

v1.2.1

Published

Load data from Goodreads in Astro

Readme

astro-loader-goodreads

npm version npm downloads License JSDocs bundle

Astro Goodreads How to use Goodreads data in Astro

Load Goodreads data for books in shelves/lists, user updates, and author blogs into Astro.

[!NOTE] astro-loader-goodreads uses the Astro Content Loader API to fetch data from Goodreads RSS feeds.

See the Usage section below for instructions on how to use the package to load the fetched Goodreads data into Astro Content Collections.


Table of Contents


Features

  • Load Bookshelves: Import your Goodreads shelves to showcase your reading list, books you've read, or want to read.
  • User Updates: Display your latest Goodreads activity including reading status updates, reviews, and likes.
  • Author Blogs: Fetch author blogs from Goodreads to display updates from your favorite authors.
  • Astro Content Collections: Seamlessly integrates with Astro's content collection system for type-safe data access.

Community Examples

Below are some examples of websites that use astro-loader-goodreads. If you wish to add your site to this list, open a pull request!

| Site | Page | Description | Source | | ------------------------------ | ------------------------------------------ | ------------------------------------------------------- | --------------------------------------- | | sadman.ca | sadman.ca/about | Books I'm currently reading and have recently finished. | |


Installation

npm add astro-loader-goodreads

Usage

Loader Options

| Property | Description | Required | Default | | --------------------- | ----------------------------------------- | ---------- | ------- | | url | The URL of your Goodreads shelf, user, or author. | ✅ | - | | refreshIntervalDays | Number of days to cache data before fetching again from Goodreads. | ❌ | 0 |

When refreshIntervalDays is set (e.g., to 7 for weekly updates), the loader will only fetch new data from Goodreads when that many days have passed since the last fetch. feat: Add optional loader option refreshIntervalDays. If not specified, no caching is done between builds (Astro's default data caching between page loads still applies).

Defining & Using Astro Content Collections

astro-loader-goodreads supports loading Goodreads data from 3 types of urls:

  1. Shelves: Load books from a Goodreads shelf.
  2. User Updates: Load a Goodreads user's updates feed.
  3. Author Blogs: Load a Goodreads author's blog.

In your content.config.ts or src/content/config.ts file, you can define your content collections using each type of URL with the goodreadsLoader function.

[!NOTE] For the full list of fields available for Astro content collections created using astro-loader-goodreads, see the Data Schema section below.


1. Goodreads Shelves

To load data for books from a Goodreads shelf, use the shelf's URL (e.g. https://www.goodreads.com/review/list/152185079-sadman-hossain?shelf=currently-reading). astro-loader-goodreads will convert it to the correct RSS feed URL automatically.

[!IMPORTANT] The RSS feed for a Goodreads shelf only includes the last 100 books added to that shelf. This means that if there are more than 100 books in a shelf, astro-loader-goodreads will not be able to retrieve them all.

You can, however, create multiple shelves (e.g. read-2025, read-2026, etc.) and then create a content collection for each shelf to get around this limitation.

// src/content/config.ts
import { defineCollection } from "astro:content";
import { goodreadsLoader } from "astro-loader-goodreads";

const currentlyReading = defineCollection({
  loader: goodreadsLoader({
    url: "https://www.goodreads.com/review/list/152185079-sadman-hossain?shelf=currently-reading",
    refreshIntervalDays: 7, // optional parameter; set to only fetch new data once per week
  }),
});

export const collections = { currentlyReading };
---
// src/pages/reading.astro
import { getCollection } from "astro:content";

const books = await getCollection("currentlyReading");
---

<h1>Books I'm Currently Reading</h1>

<div class="book-grid">
  {books.map((book) => (
    <div class="book-card">
      <img src={book.data.book_large_image_url} alt={`Cover of ${book.data.title}`} />
      <h2>{book.data.title}</h2>
      <p class="author">by {book.data.author_name}</p>
      {book.data.user_rating > 0 && (
        <p class="rating">My rating: {book.data.user_rating}/5</p>
      )}
      <a href={book.data.link} target="_blank" rel="noopener noreferrer">
        View on Goodreads
      </a>
    </div>
  ))}
</div>

<style>
  .book-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 2rem;
  }
  .book-card {
    border: 1px solid #eee;
    border-radius: 0.5rem;
    padding: 1rem;
    text-align: center;
  }
  .book-card img {
    max-width: 100%;
    height: auto;
  }
</style>

2. Goodreads User Updates

To load a Goodreads user's updates feed, use the user's profile URL (e.g. https://www.goodreads.com/user/show/152185079-sadman-hossain). astro-loader-goodreads will convert it to the correct RSS feed URL automatically.

[!IMPORTANT] The RSS feed for a Goodreads user's updates only includes the last 10 updates by that user. This means that astro-loader-goodreads cannot retrieve more than 10 updates for any single user.

// src/content/config.ts
import { defineCollection } from "astro:content";
import { goodreadsLoader } from "astro-loader-goodreads";

const userUpdates = defineCollection({
  loader: goodreadsLoader({
    url: "https://www.goodreads.com/user/show/152185079-sadman-hossain",
  }),
});

export const collections = { userUpdates };
---
// src/pages/activity.astro
import { getCollection } from "astro:content";

const updates = await getCollection("userUpdates");

// Sort updates by publication date (newest first)
const sortedUpdates = updates.sort((a, b) => 
  new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime()
);
---

<h1>My Goodreads Activity</h1>

<div class="activity-feed">
  {sortedUpdates.map((update) => {
    const itemData = update.data.itemData;
    
    return (
      <div class="activity-item">
        <p class="date">{new Date(update.data.pubDate).toLocaleDateString()}</p>
        
        {itemData?.type === "ReadStatus" && (
          <div class="read-status">
            <img 
              src={itemData.bookImgUrl} 
              alt={`Cover of ${itemData.bookTitle}`} 
              width="50"
            />
            <div>
              <p>
                <strong>{itemData.readingStatus}</strong> 
                <a href={itemData.bookUrl}>{itemData.bookTitle}</a> 
                by {itemData.bookAuthor}
              </p>
            </div>
          </div>
        )}
        
        {itemData?.type === "Review" && (
          <div class="review">
            <img 
              src={itemData.bookImgUrl} 
              alt={`Cover of ${itemData.bookTitle}`} 
              width="50"
            />
            <div>
              <p>
                Rated <strong>{itemData.rating} stars</strong> for 
                <a href={itemData.bookUrl}>{itemData.bookTitle}</a> 
                by {itemData.bookAuthor}
              </p>
            </div>
          </div>
        )}
        
        {itemData?.type === "CommentReview" && (
          <div class="comment-review">
            <div>
              <p>
                Commented on <a href={itemData.reviewUrl}>{itemData.reviewUser}'s review</a> of 
                <a href={itemData.bookUrl}>{itemData.bookTitle}</a>:
              </p>
              <blockquote>"{itemData.comment}"</blockquote>
            </div>
          </div>
        )}
        
        {/* Add additional item types as needed */}
      </div>
    );
  })}
</div>

3. Goodreads Author Blogs

To load Goodreads author blog posts, use the author's URL (e.g. https://www.goodreads.com/author/show/3389.Stephen_King). astro-loader-goodreads will append the necessary parameters to fetch the blog RSS feed automatically.

// src/content/config.ts
import { defineCollection } from "astro:content";
import { goodreadsLoader } from "astro-loader-goodreads";

const authorBlog = defineCollection({
  loader: goodreadsLoader({
    url: "https://www.goodreads.com/author/show/3389.Stephen_King",
  }),
});

export const collections = { authorBlog };
---
// src/pages/author-updates.astro
import { getCollection } from "astro:content";

const posts = await getCollection("authorBlog");
---

<h1>Latest Updates from Stephen Kingn</h1>

<div class="blog-posts">
  {posts.map((post) => (
    <article class="blog-post">
      <h2>{post.data.title}</h2>
      <p class="date">Published: {new Date(post.data.pubDate).toLocaleDateString()}</p>
      {post.data.content && (
        <div class="content" set:html={post.data.content} />
      )}
      <a href={post.data.link}>Read on Goodreads</a>
    </article>
  ))}
</div>

Data Schema

Overview

The astro-loader-goodreads package provides three main schemas:

  1. BookSchema - For books from Goodreads shelves
  2. UserUpdateSchema - For user updates (with various activity types)
  3. AuthorBlogSchema - For author blog posts

1. BookSchema

This schema is used when loading data from a Goodreads shelf.

export const BookSchema = z.object({
  id: z.coerce.string(),
  title: z.coerce.string(),
  guid: z.string(),
  pubDate: z.string(),
  link: z.string(),
  book_id: z.coerce.string(),
  book_image_url: z.string(),
  book_small_image_url: z.string(),
  book_medium_image_url: z.string(),
  book_large_image_url: z.string(),
  book_description: z.string(),
  num_pages: z.string().optional(),
  author_name: z.string(),
  isbn: z.coerce.string(),
  user_name: z.string(),
  user_rating: z.number(),
  user_read_at: z.string(),
  user_date_added: z.string(),
  user_date_created: z.string(),
  user_shelves: z.string().optional(),
  user_review: z.string().optional(),
  average_rating: z.number(),
  book_published: z.coerce.string(),
});

Book Fields

| Field | Description | |-------|-------------| | id | Unique identifier for the book | | title | Book title | | guid | Global unique identifier for this entry | | pubDate | Publication date of this entry in the feed | | link | URL to the book's Goodreads page | | book_id | Goodreads ID for the book | | book_image_url | URL to the book cover image | | book_small_image_url | URL to small version of book cover (50×75 px) | | book_medium_image_url | URL to medium version of book cover (65×98 px) | | book_large_image_url | URL to large version of book cover (316×475 px) | | book_description | Description/synopsis of the book | | num_pages | Number of pages in the book (optional) | | author_name | Name of the book's author | | isbn | International Standard Book Number | | user_name | Username of who added the book to their shelf | | user_rating | Rating given by the user (0-5) | | user_read_at | Date when the user finished reading the book | | user_date_added | Date when the book was added to the user's shelf | | user_date_created | Date when this entry was created | | user_shelves | List of shelves the user assigned to this book (optional) | | user_review | User's review of the book (optional) | | average_rating | Average rating on Goodreads | | book_published | Book's original publication date |

2. UserUpdateSchema

This schema is used when loading data from a Goodreads user's updates feed.

export const UserUpdateSchema = z.object({
  id: z.string(),
  title: z.string(),
  link: z.string().optional(),
  description: z.string().optional(),
  pubDate: z.string(),
  itemType: z.string().optional(),
  itemData: ItemDataSchema.optional()
});

UserUpdateSchema Item Types

The itemData field contains a discriminated union based on the type field:

AuthorFollowing

When a user follows an author:

{
  type: "AuthorFollowing",
  followId: string,
  userUrl: string, 
  authorId: string
}

UserStatus

When a user reports progress on a book:

{
  type: "UserStatus",
  userUrl: string,
  percentRead: string,
  bookUrl: string,
  bookTitle: string,
  bookAuthor: string,
  bookImgUrl: string
}

ReadStatus

When a user changes their reading status:

{
  type: "ReadStatus",
  userUrl: string, 
  readingStatus: string, // 'started reading', 'wants to read', or 'finished reading'
  bookUrl: string,
  bookTitle: string,
  bookAuthor: string,
  bookImgUrl: string
}

Review

When a user posts a review:

{
  type: "Review",
  userUrl: string,
  rating: number,
  bookUrl: string,
  bookTitle: string,
  bookAuthor: string,
  bookImgUrl: string
}

LikeReview

When a user likes someone's review:

{
  type: "LikeReview",
  userUrl: string,
  reviewUrl: string,
  reviewUser: string,
  bookUrl: string,
  bookTitle: string,
  bookImgUrl: string
}

LikeReadStatus

When a user likes someone's read status:

{
  type: "LikeReadStatus",
  userUrl: string,
  readStatusUser: string,
  readStatusUserImgUrl: string,
  readStatus: string,
  bookUrl: string,
  bookTitle: string
}

CommentStatus

When a user comments on a status:

{
  type: "CommentStatus",
  userUrl: string,
  statusUrl: string,
  statusUser: string,
  comment: string
}

CommentReview

When a user comments on a review:

{
  type: "CommentReview",
  userUrl: string,
  reviewUrl: string,
  reviewUser: string,
  bookUrl: string,
  bookTitle: string,
  bookAuthor: string,
  comment: string
}

3. AuthorBlogSchema

This schema is used when loading data from a Goodreads author's blog.

export const AuthorBlogSchema = z.object({
  id: z.string(),
  title: z.string(),
  link: z.string(),
  description: z.string(),
  pubDate: z.string(),
  author: z.string().optional(),
  content: z.string().optional(),
});

AuthorBlogSchema Fields

| Field | Description | |-------|-------------| | id | Unique identifier for the blog post | | title | Blog post title | | link | URL to the blog post | | description | Raw HTML description of the blog post | | pubDate | Publication date | | author | Author's name (if available) | | content | Main content of the blog post (if available) |


License

astro-loader-goodreads is MIT licensed.


Built with ♥ by @sadmanca!