Anatomy of a shields.io badge URL
Shields.io badges follow the structure: https://img.shields.io/{type}/{parameters}?{options}. The type determines the data source (github, npm, badge) and parameters vary by type. For example, a GitHub actions badge uses /github/actions/workflow/status/username/repo/workflow.yml, while a static badge uses /badge/label-message-color.
Most useful query string options: style= (flat, flat-square, plastic, for-the-badge, social), logo= (simple-icons name), logoColor= (hex without #), label= (override left text), color= (hex or named like brightgreen, blue, red). Complete example: ?style=flat-square&logo=github&logoColor=white&label=build.
For dynamic badges (that query APIs), shields.io normally caches results for 5 minutes. You can force reload by adding &cacheBuster=timestamp. Static badges are immediate because they don't query anything external. If a badge is slow to load, likely the external API is slow or the project doesn't exist at that exact path.
README badge strategy: which to include and which to avoid
An effective README shows 4-8 badges maximum at the top: build status (critical), test coverage (if +70%), current version, and license. Everything else is secondary and can go in specific sections or be omitted. Too many badges at the top create 'banner blindness' and make visitors ignore all of them.
Badges that DO add value: build status (shows if project works), coverage (indicates test quality), version (helps know if stable), downloads/month (validates popularity), last commit (indicates recent activity), license (key legal information). Badges that DON'T add real value: 'made with love', badges of all languages used if obvious from code, multiple badges of the same thing (you don't need npm downloads daily, weekly AND monthly).
For enterprise projects, prioritize reliability badges: uptime, security scanning (Snyk, Dependabot), compliance (SOC2, HIPAA if applicable). For community open source projects, prioritize social badges: contributors, PRs welcome, Discord/chat. Context determines which badges communicate seriousness versus accessibility.
Creating custom badges: static vs dynamic
Static badges use the syntax: . Replace spaces with %20 or hyphens. Colors can be hex (without #) or names: brightgreen, green, yellowgreen, yellow, orange, red, blue, lightgrey, blueviolet, ff69b4. Example: /badge/coverage-85%25-brightgreen creates a badge saying 'coverage 85%'.
Dynamic badges with endpoint.json: shields.io can query any public JSON endpoint. Format: . Your endpoint must return: {"schemaVersion":1,"label":"custom","message":"value","color":"blue"}. Useful for own metrics: active users, transactions/day, average latency. Implement caching on your side to avoid saturating your server.
For private repository badges, some services require tokens. Shields.io supports this via query params but exposes the token publicly in your README. Better solution: generate the badge server-side in your CI and upload as artifact, or use services like Codecov that handle private repos with webhooks. Never hardcode access tokens in public URLs.
Automation: generating badges in CI/CD
In your GitHub Actions workflow, generate coverage badges automatically: after running tests with jest --coverage, use an action like jest-coverage-badges that parses coverage/coverage-summary.json and updates a badge SVG in the repo. Commit the SVG and reference it in README as . This avoids external dependencies and works in private repos.
For performance badges (bundle size, lighthouse scores), tools like bundlesize and lighthouse-ci can generate badges automatically. Example: run Lighthouse in CI, extract the score, post it to an endpoint that returns shields.io JSON, and reference it in README. The badge updates on every merge to main.
Common mistakes: forgetting to update badges when changing repo name (URLs break), not configuring permissions for CI to write to repo (badges don't update), or generating badges in feature branches that never merge (nobody sees them). Configure badges only on main branch and make the process part of automatic merge using branch protection rules.