GitHub button
An outline pill linking to a repo, with the GitHub mark and a formatted star count.
npx shadcn@latest add https://ui.goodtabs.co/r/github-button.json
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/github-button
Repo name as label
Pass the repo as the label, in both sizes.
Count only
label null hides the text; no stars hides the count.
Usage
import { GitHubButton } from "@/components/ui/github-button"
<GitHubButton repo="shadcn-ui/ui" stars={98400} />
<GitHubButton repo="vercel/next.js" label="vercel/next.js" stars={136200} size="sm" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| repo | string | The repository as owner/name. Builds the href. | |
| stars | number | Star count, formatted as 1.2k. Leave out to hide it. | |
| label | string | null | "Star" | Visible label. Pass the repo name, or null for mark and count only. |
| size | "sm" | "default" | "default" | 28px or 32px tall. |
Source
github-button/github-button.tsx
import * as React from "react";
import { StarIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
/** 1200 becomes 1.2k, 2400000 becomes 2.4m. Below 1000 the number is shown as is. */
export function formatStars(stars: number): string {
if (stars < 1000) return String(stars);
const units: [number, string][] = [
[1_000_000, "m"],
[1_000, "k"],
];
const [divisor, suffix] = units.find(([d]) => stars >= d) ?? [1, ""];
const value = stars / divisor;
const rounded = value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
return `${rounded}${suffix}`;
}
type GitHubButtonProps = Omit<React.ComponentProps<"a">, "href" | "children"> & {
/** The repository as "owner/name". */
repo: string;
/** Star count. Leave out to hide the count. */
stars?: number;
/** Visible label. Pass the repo name to show it, or null for the mark and count only. */
label?: string | null;
/** 28px or 32px tall. */
size?: "sm" | "default";
};
function GitHubMark(props: React.ComponentProps<"svg">) {
return (
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
);
}
/**
* An outline pill that links to a GitHub repo, with the mark, a label and a formatted star count.
* Pass the count yourself: the component never fetches.
*/
export function GitHubButton({
repo,
stars,
label = "Star",
size = "default",
className,
...props
}: GitHubButtonProps) {
const hasCount = typeof stars === "number";
return (
<Button asChild variant="outline" size={size} className={cn("rounded-full", className)}>
<a href={`https://github.com/${repo}`} target="_blank" rel="noreferrer" {...props}>
<GitHubMark data-icon="inline-start" />
{label && <span>{label}</span>}
{hasCount && (
<span className="flex items-center gap-1" aria-hidden="true">
{label && <span className="mx-0.5 h-3.5 w-px bg-border" />}
<StarIcon className={size === "sm" ? "size-3" : "size-3.5"} />
<span className="font-mono text-xs tabular-nums">{formatStars(stars)}</span>
</span>
)}
<span className="sr-only">
{label ? ` ${repo} on GitHub` : `${repo} on GitHub`}
{hasCount ? `, ${new Intl.NumberFormat("en-US").format(stars)} stars` : ""}
</span>
</a>
</Button>
);
}
For AI agents
Hand your agent this page, the markdown at /components/github-button/prompt.md, or the Copy AI prompt button above.
When to use
- In a header or hero of an open source project, next to the primary action
- In a footer or docs page where the repo is the canonical link
- Anywhere a star count is a useful signal and you already have the number
When not to use
- When you cannot supply the count: it never fetches, and a stale number is worse than none
- For a generic external link, use a plain outline Button with an anchor
- For social profile links, use social-links
Agent notes
- Pass stars from your own fetch (the GitHub API's stargazers_count); formatStars is exported if you need the same format elsewhere
- It is shadcn's Button with asChild around an anchor, opening in a new tab with rel noreferrer
- The accessible name is the label plus 'owner/name on GitHub, 1,234 stars', so icon-only use still reads well
- The GitHub mark is an inline SVG in currentColor, since lucide ships no brand icons