# RSS Feed Widget

A lightweight, zero-dependency vanilla JavaScript library for embedding RSS feeds on any website. Under 7KB gzipped, with dark mode, WCAG AA accessibility, pagination, and XSS sanitization built in.

**[Live Demo](https://basicallyhowtodo.github.io/RSS-Feed-Widgets)** · **[Full Docs and Examples](https://how-to-do.net/rss-feed-widget)**

## Features

- **Zero dependencies** with no frameworks, no build step, and no bundler config
- **Under 7KB** gzipped (JS + CSS combined)
- **Pagination** with a load-more button or numbered page navigation
- **Image support** that automatically displays thumbnails from RSS enclosure tags
- **Responsive** layout that looks great on mobile, tablet, and desktop
- **Dark mode** that auto-detects system preference or can be set manually
- **Category filters** auto-generated from your feed's categories
- **Skeleton loading** with a smooth shimmer state while fetching
- **Auto-colored badges** with consistent colors generated from category names
- **XSS protection** that sanitizes all feed data before rendering anything to the DOM
- **CORS handling** with built-in proxy support for cross-origin feeds
- **Accessible** with WCAG AA contrast, ARIA attributes, keyboard navigation, and prefers-reduced-motion support
- **Universal module** that works as a script tag, CommonJS require, or AMD define
- **Configurable** with options for card count, description length, theme, images, pagination, and more

## Installation

Install via npm for managed projects, or drop in a CDN link for instant setup. No build step, no bundler, no config files required.

**npm**

```
npm install rss-feed-widget
```

**jsDelivr CDN**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rss-feed-widget@1.2.0/rss-widget.css">
<script src="https://cdn.jsdelivr.net/npm/rss-feed-widget@1.2.0/rss-widget.js"></script>
```

**unpkg CDN**

```html
<link rel="stylesheet" href="https://unpkg.com/rss-feed-widget@1.2.0/rss-widget.css">
<script src="https://unpkg.com/rss-feed-widget@1.2.0/rss-widget.js"></script>
```

**Direct download**

```
git clone https://github.com/BasicallyHowToDo/RSS-Feed-Widgets.git
```

## Quick Start

Three steps to a working feed widget. No registration, no API key, no server-side code. The widget fetches, parses, and renders your feed entirely in the visitor's browser.

```html
<link rel="stylesheet" href="rss-widget.css">
<script src="rss-widget.js"></script>

<div id="rss-widget"></div>

<script>
  RSSWidget.init({
    feed: 'https://how-to-do.net/feed.xml',
    container: '#rss-widget'
  });
</script>
```

That's it. Two files, three lines.

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `feed` | string | `'https://how-to-do.net/feed.xml'` | RSS feed URL |
| `container` | string | `'#rss-widget'` | CSS selector for the container element |
| `perPage` | number | `9` | Number of items shown per page or load |
| `maxItems` | number | `0` | Total cap across all pages, 0 means no limit |
| `pagination` | boolean | `false` | Enable pagination controls below the grid |
| `paginationStyle` | string | `'button'` | `'button'` for load more or `'numbered'` for page numbers |
| `showFilter` | boolean | `true` | Show category filter buttons |
| `showDate` | boolean | `true` | Show publication date on cards |
| `showImage` | boolean | `true` | Show images from RSS enclosure tags |
| `descriptionLength` | number | `120` | Truncate descriptions to this length |
| `theme` | string | `'auto'` | `'auto'`, `'light'`, or `'dark'` |
| `corsProxy` | string | `null` | Proxy URL prefix for CORS (e.g. `'https://api.allorigins.win/raw?url='`) |
| `skeletonCount` | number | `6` | Number of skeleton cards during loading |
| `onLoad` | function | `null` | Callback when feed loads successfully |
| `onError` | function | `null` | Callback when feed fails to load |

## Pagination

The widget supports two pagination styles that you can enable with the `pagination` option. When pagination is off (the default), the widget shows `perPage` items with no controls, which keeps behavior simple for smaller feeds.

**Load more** shows a pill-shaped button below the grid that appends the next batch of items each time you click it. The button displays how many items remain and disappears once everything is visible.

```javascript
RSSWidget.init({
  feed: 'https://how-to-do.net/feed.xml',
  container: '#rss-widget',
  perPage: 9,
  pagination: true,
  paginationStyle: 'button'
});
```

**Numbered pages** renders a row of page buttons with previous and next arrows at each end. Clicking a page number replaces the grid content and smooth-scrolls back to the top of the widget so you never lose your place.

```javascript
RSSWidget.init({
  feed: 'https://how-to-do.net/feed.xml',
  container: '#rss-widget',
  perPage: 9,
  pagination: true,
  paginationStyle: 'numbered'
});
```

Both styles work with category filters. Selecting a category resets to the first page and recalculates the page count based on items in that category. The `maxItems` option caps the total pool of items before any filtering or pagination happens, so if your feed has 100 items and you set `maxItems` to 50 only the first 50 items are considered.

## Images

The widget automatically displays images from your RSS feed's `<enclosure>` tags. If your feed includes image enclosures like this:

```xml
<item>
  <title>How to Make Simple Syrup</title>
  <enclosure url="https://example.com/image.jpg" type="image/jpeg" length="0" />
</item>
```

The widget renders them as full-width card images with a 16:9 aspect ratio, object-fit cover, and a subtle zoom on hover.

To disable images, set `showImage: false` in your config.

Feeds without enclosure tags render cards without images, exactly like v1.0.0.

## Theming

The widget uses CSS custom properties for all colors. Override them to match your design.

```css
:root {
  --rss-accent: #4f46e5;
  --rss-radius: 14px;
  --rss-surface: #ffffff;
  --rss-border: #e8e8e8;
  --rss-text: #1a1a2e;
}
```

Dark mode activates automatically when the user's system preference is dark, or set `theme: 'dark'` to force it.

## CORS

If the RSS feed you are loading has `Access-Control-Allow-Origin: *` set (like the default how-to-do.net feed), it works directly with no proxy needed.

For feeds without CORS headers, pass a proxy.

```javascript
RSSWidget.init({
  feed: 'https://example.com/feed.xml',
  corsProxy: 'https://api.allorigins.win/raw?url='
});
```

## Browser Support

Works in all modern browsers (Chrome, Firefox, Safari, Edge). Uses `DOMParser`, `fetch`, and CSS Grid which are supported in all browsers released after 2018.

## Links and Resources

- [npm](https://www.npmjs.com/package/rss-feed-widget) – Package registry
- [GitHub](https://github.com/BasicallyHowToDo/RSS-Feed-Widgets) – Source code and issues
- [Product Hunt](https://www.producthunt.com/products/rss-feed-widget) – Product listing and reviews
- [SourceForge](https://sourceforge.net/projects/rss-feed-widget/) – Alternative download
- [Dev.to Tutorial](https://dev.to/debate_me_af4b65ae011518f/build-a-simple-rss-feed-widget-in-vanilla-javascript-1803) – Build guide and walkthrough
- [HackerNoon Article](https://hackernoon.com/why-rss-still-beats-apis-for-content-distribution) – Why RSS still beats APIs

## Changelog

### v1.2.0
- Added pagination with two styles, a load-more button and numbered page navigation
- New `perPage`, `pagination`, and `paginationStyle` config options
- Category filters now reset to page 1 and recalculate pagination for the active category
- The `maxItems` default changed from 12 to 0 so all feed items are available by default
- Internal architecture moved from toggling card visibility to a state-driven re-render through `refreshView()`
- Full accessibility support on all pagination controls with ARIA attributes and keyboard navigation
- Smooth scroll to grid top on numbered page changes, respecting prefers-reduced-motion

### v1.1.0
- Added image support via RSS `<enclosure>` tags
- New `showImage` config option (default: `true`)
- Full-width card images with 16:9 aspect ratio
- Hover zoom animation on images (respects prefers-reduced-motion)
- Skeleton loading includes image placeholder

### v1.0.0
- Initial release

## License

MIT

---

Default demo feed provided by [how-to-do.net](https://how-to-do.net) · Built with vanilla JS · [View on GitHub](https://github.com/BasicallyHowToDo/RSS-Feed-Widgets)
