How to Display Live South African Tender Awards on Your Website
The Tenders-SA Winners Feed widget lets you embed a live feed of recently awarded government tenders on any website. This guide covers the architecture, embed methods, configuration options, and links to the open-source repository.
Why Display Live Tender Award Data?
Every week, South African government departments publish tender awards running into billions of rand. For suppliers, industry analysts, and procurement professionals, tracking who won which contract is essential market intelligence. But keeping up with this data is difficult — awards are scattered across different portals, PDF notices, and press releases.
The Winners Feed widget (github.com/Tenders-SA/widget-winners-feed) solves this by providing an embeddable, auto-updating feed of the most recent tender awards. It is an open-source, zero-dependency JavaScript widget that you can add to any website with a single HTML snippet. This post walks through how the widget works, how to embed it, and how to customise it.
What Data Does the Widget Display?
The widget fetches the 100 most recent awarded tenders from the /api/widgets/winners-feed endpoint and renders them in a scrollable list. Each award entry shows:
| Field | Type | Description |
|---|---|---|
| supplierName | string | The winning supplier or contractor |
| amount | number | Awarded contract value in ZAR |
| date | string | Date the award was published |
| orgName | string | Government department or municipality that issued the tender |
| province | string | Province where the contract is managed |
Values are formatted using compact number notation (R 1.5M, R 250K) for readability. The list is sorted by award date, most recent first, and capped at 100 entries to keep the widget lightweight.
Architecture: How the Widget Works
The widget is a vanilla TypeScript class compiled to a self-contained JavaScript bundle via tsup. It has no external dependencies — no jQuery, no React, no framework required. The core architecture follows three layers:
- Data layer (
api.ts) — AfetchWinnersFeed()function that calls the API endpoint with anAbortSignalfor cancellable requests. The API base URL defaults tohttps://tenders-sa.orgbut can be overridden. - Rendering layer (
renderer.ts) — A DOM-based renderer that builds the widget tree node by node. It injects styles into the document head (deduplicated by ID) and constructs the widget skeleton — header with branded dot indicator, scrollable award list, and a footer CTA. - Widget class (
index.ts) — TheWinnersFeedWidgetclass orchestrates the lifecycle: constructor (reads data attributes and config),render()(shows loading state, fetches data, delegates to renderer), anddestroy()(cancels in-flight requests and clears the DOM).
The widget also includes an auto-initialisation function that scans the DOM for [data-tendersa-winners-feed] elements and instantiates a WinnersFeedWidget for each. This runs on DOMContentLoaded (or immediately if the DOM is already ready), which is how the script-tag embed method works without any JavaScript setup.
Embed Methods
There are two ways to embed the widget, depending on whether you want a simple script-tag approach or programmatic control.
Method 1: HTML Data Attribute (Recommended)
Add a div with the data-tendersa-winners-feed attribute and include the script. That is all you need.
1<!-- Place the container where you want the feed to appear --> 2<div data-tendersa-winners-feed data-theme="light"></div> 3 4<!-- Include the widget script (async recommended) --> 5<script 6 src="https://unpkg.com/@tendersa/widget-winners-feed@latest/dist/widget-winners-feed.global.js" 7 async 8></script>HTML
The widget auto-initialises when the script loads. The optional data-theme attribute switches between light and dark themes. If omitted, the widget respects the user's prefers-color-scheme media query.
Method 2: Programmatic (NPM / Bundler)
Install the package via npm for use in a JavaScript build pipeline:
1npm install @tendersa/widget-winners-feedBASH
Then instantiate the widget programmatically:
1import { WinnersFeedWidget } from '@tendersa/widget-winners-feed' 2 3const container = document.getElementById('my-widget-container') 4const widget = new WinnersFeedWidget(container, { 5 theme: 'dark', 6}) 7 8widget.render().then(() => { 9 console.log('Winners feed rendered') 10}) 11 12// Clean up when no longer needed (e.g. in a SPA navigation) 13// widget.destroy()JAVASCRIPT
The programmatic method is the right choice if you are building a single-page application (React, Vue, Svelte, etc.) and need to control the widget lifecycle manually. Call destroy() in your component's cleanup phase to cancel pending network requests and remove DOM nodes.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
| theme | 'light' | 'dark' | System preference |
| apiBase | string | 'https://tenders-sa.org' | Base URL for the API endpoint (useful for self-hosted deployments) |
When using the data-attribute method, set data-theme="dark" on the container element. When using the programmatic method, pass the options in the constructor's second argument.
Browser Support and Performance
The widget uses standard DOM APIs (document.querySelectorAll, fetch, AbortController, DOMContentLoaded) and ES2015+ features. It supports all modern browsers — Chrome, Firefox, Safari, and Edge. Internet Explorer is not supported.
Performance characteristics:
- Bundle size: approximately 6 KB (minified and gzipped)
- API response time: typically under 200 ms server-side (the endpoint uses ISR with 3600-second revalidation)
- Render time: under 50 ms on desktop after data is received
- No layout shift: the container element reserves space; the loading state displays immediately
The Open Source Package
The Winners Feed widget is published as @tendersa/widget-winners-feed on npm and is open source under the MIT license. The source code is available at github.com/Tenders-SA/widget-winners-feed.
The repository includes:
- Full TypeScript source with type definitions exported
- Unit tests via Vitest
- Build pipeline via tsup (outputs ESM, CJS, and IIFE bundles)
- Comprehensive README with embed examples
- Issues and pull requests welcome
If you are interested in the underlying platform API, the endpoint /api/widgets/winners-feed returns a JSON array of recent awards and is also accessible independently (CORS-enabled with Access-Control-Allow-Origin: *).
Getting Started
To add the winners feed to your site:
- Add a
<div data-tendersa-winners-feed></div>element to your HTML where you want the feed to appear. - Include the script tag pointing to the unpkg CDN (or host the file yourself from the dist directory).
- Optionally add
data-theme="dark"if your site uses a dark colour scheme. - That is it — the widget auto-initialises on page load.
The source code and documentation are on GitHub at github.com/Tenders-SA/widget-winners-feed. Issues, feature requests, and pull requests are welcome. If you are building on top of SA procurement data, you may also find the Tenders-SA JavaScript SDK useful for accessing the full Developer API.
Tags
Based on this article's topics, here are some current tenders that might interest you
Request for Proposals (RFP) The Provision of Underwater Measurement Capabilities and Technical Expertise Related to the Development and Testing of Maritime Technology and Underwater Sensors with the CSIR for a Period of 5 Years.
APPOINTMENT OF A SERVICE PROVIDER TO RENDER THE INFORMATION TECHNOLOGY MANAGEMENT SERVICES FOR A PERIOD OF TWO (2) YEARS.
Want to see all available tenders?
Browse All Tenders →Share this article
How to Display Live South African Tender Awards on Your Website
The Tenders-SA Winners Feed widget lets you embed a live feed of recently awarded government tenders on any website. This guide covers the architecture, embed methods, configuration options, and links to the open-source repository.