Create custom badges with NextJS (shields)
In the GitHub Repo README it is useful to have Badges at the top to display the latest information.
We have all seen GitHub Repo README with badges at the top. But do you know how to create one to add to your project, so that contributors can use it?
What are we going to build?
In this post I am going to show you how to do this and as an example, I will add badges (which can also be known as Shields) to the EddieHub project “The Open Source HealthCheck” https://github.com/EddieHubCommunity/HealthCheck
The feature will enable people to proudly show off that their Open Source project is friendly.
If you prefer, you can watch this tutorial - the video is at the end of this post.
What are we going to use?
NextJS (app router)
Badge Maker https://github.com/badges/shields
Pre-requisites
Existing NextJS project
Npm
Installation
Install the npm dependency.
npm install badge-maker
Import the dependency in your API file in NextJS.
// src/app/api/badges/report/[id]/route.js
import { makeBadge } from "badge-maker";
The default badge config will be used if no data exists for the GitHub repo yet in "The Open Source HealthCheck” project.
let config = {
message: "No checks",
label: "HealthCheck",
style: "flat",
};
Get the checks from the database using the uid (we are using Prisma but you can use any tool you prefer to manage your data).
const check = await prisma.check.findUnique({
where: { id },
});
Customise the badge color and message depending on “The Open Source HealthCheck” results.
config = {
...config,
message:
check.red > 0
? `Error (${check.red})`
: check.amber > 0
? `Warning (${check.amber})`
: `Success (${check.green})`,
color: check.red > 0 ? "red" : check.amber > 0 ? "orange" : "green",
};
let svg = "";
try {
svg = makeBadge(config);
} catch (e) {
console.log(e);
// TODO: return error badge
}
return new Response(svg, {
headers: {
"Content-Type": "image/svg+xml",
},
});
Why the nested ternary on message and color? We check for the worst situation first, which is error (red), then work our way through warning (amber) to success (green). If any results exist along this journey, that is the color and message that will be shown.
Then we create the SVG badge with “makeBadge” and return the response body to have the SVG string as well as the content type of “image/svg+xml”.
The result
Using an example Repo, we get the following badge locally
import { makeBadge } from "badge-maker";
import prisma from "@/models/db";
import { worstCheck } from "@/utils/checks";
export const dynamic = "force-dynamic";
export async function GET(request, { params }) {
const { id } = params;
// get repo data
const check = await prisma.check.findUnique({
where: { id },
});
// default badge settings
let config = {
message: "No checks",
label: "HealthCheck",
style: "flat",
};
if (check) {
// if check report found
config = {
...config,
message: worstCheck(
check,
`Error (${check.red})`,
`Warning (${check.amber})`,
`Success (${check.green})`
),
color: worstCheck(check, "red", "orange", "green"),
};
} else {
// if check report not found
config = {
...config,
message: "Not found",
color: "lightgrey",
};
}
let svg = "";
try {
svg = makeBadge(config);
} catch (e) {
console.log(e);
// TODO: return error badge
}
return new Response(svg, {
headers: {
"Content-Type": "image/svg+xml",
},
});
}
Future tasks
Now that was not so scary! I know when I first created a badge it was challenging, even though there was not much code involved.
You may notice that this will be re-generated on every visit which is not efficient. This can be improved with caching. Especially as the data does not change often, therefore caching the data for a short period of time would make the app more efficient.