# Green Wall: a Next.js app that renders your GitHub contribution graph as a shareable image

> Green Wall reads the GitHub GraphQL API, draws a contribution wall across multiple years, and exposes it as an image endpoint you can drop into a README, plus an optional AI yearly report over the same data. It is MIT licensed, ships a Tampermonkey script for viewing anyone's profile, and can be self-hosted with Docker or one click on Vercel.

**Codennnn/Green-Wall** — ⬜🟩 GitHub Contributions Graph Generator.  :octocat:

- Repository: https://github.com/Codennnn/Green-Wall
- Website: https://green-wall.leoku.dev
- Stars: 811 · Forks: 43
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/codennnn-green-wall

## The image endpoint is the actual product

Green Wall is a web tool for generating contribution images and AI-powered yearly reports from your GitHub activity, and the way it is used is a plain image tag. The HTML form is:

```html
<img
  src="https://green-wall.leoku.dev/api/og/share/[YOUR_USERNAME]"
  alt="My contributions"
/>
```

And the Markdown form is one line:

```markdown
![](https://green-wall.leoku.dev/api/og/share/[YOUR_USERNAME])
```

The endpoint path is the same in both cases, an og route under api taking a username. That matters because the output is an ordinary image URL, so it refreshes on its own without any script on the page and without the README holding a stale copy of your graph.

There are four optional query parameters, and their defaults show what the endpoint is tuned for. The year parameter takes a number and defaults to the latest year. The theme parameter takes a string and defaults to Classic. The width defaults to 1200 and the height to 630, which are the dimensions of a link preview card, so the default output is shaped for a social preview rather than for a banner.

The data path underneath is the GitHub GraphQL API, with Next.js API routes handling the requests, and the README points at a specific file for how the data is managed, so the token handling is documented rather than left to inspection.

## The yearly report is the optional half

The second feature is an AI-powered yearly review of your contributions, described as detailed insights and summaries about your coding activity through the year, with key statistics and highlights.

It is optional in a fairly literal sense, because it is the only part that needs an AI provider. The environment variables table lists four entries. GITHUB_ACCESS_TOKEN is required and is the token the server uses to call the GitHub GraphQL API. AI_BASE_URL is optional and is the base URL of an OpenAI-compatible API endpoint, with api.openai.com/v1 given as the example. AI_API_KEY is the provider key and is marked optional with a footnote. AI_MODEL is the model name supported by your provider, with gpt-4o-mini as the example.

The note under the table is the important operational detail. The AI variables are only needed for the yearly AI report feature at the api/ai/yearly-report path, and if you set the key you must also make the base URL and the model valid. Everything else, the contribution wall and the image endpoint, runs on the GitHub token alone.

The implementation uses the Vercel AI SDK with the OpenAI-compatible provider, which is why the base URL is configurable. An OpenAI-compatible endpoint means you can point it at a local model server rather than a hosted one, which is the reason to have that variable at all.

## Deploying it yourself, and why the README pushes you to

The public demo is hosted on the Netlify Free plan, and the README warns that it may be temporarily unavailable when the monthly quota is exhausted, says that if you hit that it is not your fault, and points you to self-deploying with a one-click Vercel deployment as the recommended option. The reasoning is stated plainly: Vercel one-click deployment gives you your own stable URL and avoids the public demo's free-tier quota limits.

The deployment steps are four, and the token step is the one to get right. You click Deploy with Vercel and authorize GitHub when prompted, with the recommendation to deploy from your own fork if you intend to modify the code. Then you create a personal access token, and the README is specific about the type: a fine-grained token is recommended for stricter permissions, selecting the minimal permissions your use case needs, and a classic token works as well. On scopes, the advice is to start minimal, and notes that in many cases read:org for organisation data and repo, if you need private contributions, are sufficient. The token must not be committed and should be treated like a password.

Then you set the environment variables during import or later under Project, Settings, Environment Variables, and finally you click Deploy and wait for the build. After the first deploy, any push to your repository triggers a new deployment automatically.

There is a verification checklist afterwards: open the deployment URL and search for a GitHub username, confirm the contributions graph loads for multiple years, check that api/contribution/<username> returns JSON and not a Bad credentials error, and confirm that api/og/share/<username> returns an image response. That checklist is a good template for testing any instance you run.

## Running it locally with pnpm or Docker

The local setup needs a personal access token, and the token goes into a file named .env.development at the project root:

```sh
# .env.development

# The format should be: GITHUB_ACCESS_TOKEN="[YOUR_TOKEN]"

# Example:
GITHUB_ACCESS_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

After that you are ready to run pnpm dev.

The Docker path is the recommended one for production. The compose file defines two services, dev and prod. The dev service builds the development target of the Dockerfile, publishes the development port, mounts the source for hot reload with an anonymous volume over node_modules so the container's own modules are preserved, mounts the .next directory to cache build artifacts, and sets the development environment with the hostname and port. The prod service builds the production target, injects NEXT_PUBLIC_SITE_URL at build time, and adds a healthcheck that runs wget with a spider flag against the local root every 30 seconds with a 40 second start period and three retries.

The Dockerfile is a multi-stage build on node:22-alpine with libc6-compat installed for npm compatibility, corepack enabled and pnpm 9.15.3 prepared, a pnpm store directory configured for caching, and separate dependency stages for full and production-only installs using a frozen lockfile and a cache mount. The development stage runs as root to avoid volume mount permission issues, while the production stage creates a non-root user with a system group.

## The stack and the extras around it

The package manifest describes a Next.js 16.3.4 application on React 19.2.7 with TypeScript, and the dependency list tells you what the front end is built from. Tailwind is there in its v4 form with the typography plugin, and lucide-react provides the icons. The charting uses recharts, and framer-motion handles animation. State and data come from @tanstack/react-query with its devtools, and next-themes handles the light and dark switching, which pairs with the theme parameter on the image endpoint.

The form and validation layer uses valibot along with class-variance-authority, clsx and tailwind-merge, and there is @base-ui-components/react at a release candidate version, which suggests the component primitives are still moving. For internationalisation there is next-intl, with a messages directory at the top level and a check-i18n script that verifies the translation files line up, and the README carries both an English and a Chinese version.

There is also better-auth in the dependency list, and html-to-image for turning the rendered graph into a downloadable image in the browser, and markdown-it plus a react-use-event-hook.

Two extras sit outside the app itself. The Tampermonkey script adds a button to any GitHub profile page so you can view that user's contribution wall, with its source in the plugins/script.ts file and its own build script and watch script in the package manifest. The repository also carries a Dockerfile, a docker-compose.yml, an AGENTS.md file, editor configuration for eslint, stylelint and prettier, and a components.json file, which is the shadcn-style configuration for the component directory.

## Where Green Wall is the wrong choice

The first limitation is the token. To use the tool at all, a GitHub personal access token has to reach a server that calls the GraphQL API on your behalf, and the README says as much with the note not to commit the token and to treat it like a password. A fine-grained token with minimal scopes limits the exposure, but the exposure still exists, because the server holding the token is a third party in the general case. That is the main reason to self-deploy, and it is a heavier reason than quota limits.

The second is that a contribution graph is a vanity signal. The tool makes a nice picture of activity, and the yearly report adds a language model summary of it, but neither says anything about whether the work was good, and the summary is generated from the same activity counts. If the goal is to reflect on your year as a developer, reading your own commit history is more informative than a generated report, and the report's value is mostly its presentation.

The third is the dependency weight. A single-page tool that draws a grid of coloured squares pulls in a chart library, an animation library, a component library at a release candidate, an i18n framework, an auth library and an AI SDK. Much of that is for the report and the themes rather than the graph. If you are building something similar inside an existing application, calling the GitHub GraphQL API and rendering the grid yourself is a small amount of work, and the image endpoint pattern here is the part worth copying.

The comparison to GitHub's own calendar is worth stating as well. The native contribution graph on the profile is rendered by GitHub itself and focuses on a single year at a time. What Green Wall adds is multiple years in one view, themes, and an embeddable image, which is genuinely more than the native view, though it is a narrow addition.

## Conclusion

Green Wall fits a developer who wants a contribution wall image in a README or a profile, and who is willing to host their own instance so the request for their GitHub data goes to a server they control rather than a shared public demo. It does not fit a team that cannot issue a GitHub token to a third party, or anyone who expects the yearly report to work without configuring an AI provider. Before deploying, read what the API route does with your token, since the README points at that file for data handling, set GITHUB_ACCESS_TOKEN as the only required variable, and remember the AI variables are only needed for the yearly report endpoint, so a self-hosted instance can run the graph feature with no provider key at all.

## FAQ

### How do I embed a Green Wall contribution graph in my README?

Use the image endpoint for your username, either as an HTML img tag pointing at /api/og/share/<username> or as a Markdown image. Optional query parameters are year, theme, width and height, with defaults of the latest year, the Classic theme, 1200 by 630.

### What environment variables does Green Wall need?

GITHUB_ACCESS_TOKEN is the only required one, used by the server to call the GitHub GraphQL API. AI_BASE_URL, AI_API_KEY and AI_MODEL are optional and only needed for the yearly AI report at the /api/ai/yearly-report endpoint, and the key is only used if the base URL and model are also valid.

### Which GitHub token type should I use with Green Wall?

The README recommends a fine-grained token with the minimal permissions your use case needs, and says a classic token works as well. On scopes it advises starting minimal, noting that read:org for organisation data and repo, if you need private contributions, are sufficient in many cases, and the token must never be committed.

### Can I run Green Wall locally?

Yes. Create a .env.development file at the project root containing GITHUB_ACCESS_TOKEN and run pnpm dev. The Docker route builds a development or production target, and the compose file mounts the source for hot reload while preserving the container's node_modules, with a healthcheck on the production service.

### Why might the public Green Wall demo be unavailable?

The public demo runs on the Netlify Free plan, so it may be temporarily unavailable when the monthly quota is exhausted. The README points to self-deploying, with a one-click Vercel deployment recommended, since that gives your own stable URL and avoids the free-tier limits.

## Sources

- [Codennnn/Green-Wall on GitHub](https://github.com/Codennnn/Green-Wall)
- [Issues](https://github.com/Codennnn/Green-Wall/issues)
- [License: MIT](https://github.com/Codennnn/Green-Wall/blob/main/LICENSE)
- [Project website](https://green-wall.leoku.dev)
- [README](https://github.com/Codennnn/Green-Wall/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/codennnn-green-wall
