# Socialify: turning a repository link into a social preview image

> Socialify is a Next.js service that renders a GitHub repository as a designed card image, either as a live URL for your README or as a downloaded file for a repository social preview. Self-hosting it needs one GitHub token.

**wei/socialify** — 💞 Socialify your project. 🌐 Share with the world!

- Repository: https://github.com/wei/socialify
- Website: http://socialify.git.ci
- Stars: 2,214 · Forks: 122
- Language: TypeScript
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/wei-socialify

## Two ways to use it, live image or downloaded file

The README splits usage into two paths and the distinction is the most important thing on the page.

The live path is an image as a service. You link to `socialify.dev/owner/repo/image` with query parameters, embed it in a README with an `img` tag, and the image is generated at request time from live repository data. The README notes that this means the badges update automatically, and it recommends this route for README files and `img` tags.

The download path gives you a file. You can download the image as a `.png`, `.jpeg` or `.webp` and use it anywhere, and the README recommends this for a GitHub repository social preview image and for other sites that require you to upload an image.

That difference has a practical consequence. A live URL is always current but depends on somebody else's service being up and on the rendering path still working. A downloaded file never changes and never breaks, but the star count and other badges are frozen at the moment you generated it. The examples in the README show exactly how the two differ, with the same repository rendered twice under different themes and parameter sets.

## Query parameters are the whole configuration surface

There is no config file and no dashboard. Everything is expressed in the URL, and reading the example links is the fastest way to learn the vocabulary. Parameters visible across the examples include `description`, `font`, `forks`, `issues`, `language`, `logo`, `owner`, `pattern`, `pulls`, `stargazers` and `theme`.

The numeric-looking ones are switches rather than values, which is not obvious from the name alone: `forks=1` or `owner=0` means show that element or do not. The `theme` parameter takes values like `Light` and `Dark`, `pattern` takes values like `Circuit Board`, `Diagonal Stripes`, `Plus`, `Floating Cogs` and `Signal`, and `font` takes names like `Bitter`, `Inter`, `Source Code Pro` and `Rokkitt`.

The `logo` parameter is the most flexible and the least obvious, because the examples show three different ways to supply one. One points at a raw GitHub URL for a PNG hosted in another repository. One points at a `gist.githack.com` URL for an SVG. One is a base64 data URI with an inline SVG. So the logo is not limited to a repository avatar, which is what makes the cards look deliberate rather than generated.

The README says only that the service includes a lot of options including custom logo, description, badges and many fonts and background patterns. It does not enumerate every parameter in prose, so the example URLs are the practical reference.

## Self-hosting with Docker and the one token you need

The README gives a Docker path first, and it is two commands:

```bash
docker-compose up -d
```

or, running Docker directly:

```bash
docker build -t socialify .
docker run -p 3000:3000 --env-file .env socialify
```

Both land the service on port 3000. The compose file in the repository matches the first command, with a single `socialify` service, `restart: unless-stopped`, a build from the repository context, the port mapping `3000:3000` and an `env_file` pointing at `.env`.

That `.env` file is the whole configuration surface for a self-hosted instance, and the example file lists three variables. `GITHUB_TOKEN` is described as a GitHub token with public repositories read-only access, with a link to the personal access tokens page. `PROJECT_URL` defaults to `http://localhost:3000` and carries a note that edge deployments need the public URL while non-edge deployments use localhost. `GTM_ID` is a Google Tag Manager identifier, which is only interesting if you want analytics on your own instance.

The Dockerfile explains the shape of the build. It starts from `node:24-alpine`, enables corepack, installs production dependencies with pnpm using a frozen lockfile, then re-installs with devDependencies to run `next build`. The final image is `gcr.io/distroless/nodejs24-debian12`, so there is no shell in the running container and only `package.json`, `node_modules`, `.next` and `public` are copied forward.

## How the rendering is built, and why there is a wasm file

The package manifest gives the implementation away. It is a Next.js app on React 19, with `next` at `^16.3.4`, and the notable dependency is `@resvg/resvg-wasm`, which is a WebAssembly build of resvg, the Rust SVG rasteriser. The scripts make the wasm part explicit: `copy:wasm` copies `index_bg.wasm` out of the node_modules package into `./public/resvg_bg.wasm`, and both `predev` and `prebuild` run it, with `postinstall` running it too.

So the image is drawn as SVG and then rasterised in the browser or on the server by a wasm module. That is what allows one rendering path to serve both PNG and other output formats, and it explains why a Next.js app with React is the right shape rather than a headless browser driving a canvas.

The other dependencies are small and tell you the same story. `badgen` generates the badge images, `hero-patterns` supplies the background patterns you select with the `pattern` parameter, and `copee` is a randomised string helper, which fits the template-style approach where each card looks slightly different unless you fix every parameter. There is no charting or image-fetching library, so the design work is vector and the network calls are plain API requests.

## Testing, versioning and the gap between package.json and releases

The tree shows a project with a serious test setup, which is worth noting because the visible product is a picture generator and the easy assumption is that there is nothing to test. There is a `jest.config.ts`, a `playwright.config.ts` and a `.playwright/` directory, and the manifest separates `test:unit` from `test:e2e`. The end-to-end suite runs `./scripts/docker-e2e.sh`, which means the image output is verified through a real container, and there are separate scripts for updating Playwright snapshots and showing the report at `./.playwright/test-report`.

Snapshot testing is the right technique for this specific product, since a card that renders one pixel differently is a bug even when nothing is functionally wrong. The `verify` script chains the whole thing together as `pnpm lint && pnpm test:unit && pnpm build`, so the lint, unit tests and production build are one command.

There is a version detail worth noticing. The manifest reads version 2.25.0, while the most recent GitHub release listed is v2.24.5, published on 2026-09-07, with v2.24.4 on 2026-09-04 and v2.24.3 on 2026-03-19. The repository is not archived and the last push was on 2026-09-14, so the default branch is ahead of the last tagged release. If you pin a version, pin to the release, not to the branch. The lint setup is biome rather than eslint, with `biome ci .` for the lint script, and there is a husky hook installed by the `prepare` script.

## The hosted service's own warning, and the case for downloading

The README has a section headed SLA and it is unusually blunt about what you do not get. It says Socialify is under active development, that design and project domain are subject to change without notice, and that you should subscribe to issue 47 if you want service updates. It then says to consider downloading the images or self-hosting should this be a problem.

That is the honest trade-off stated by the project itself. Using `socialify.dev` in your README gives you images that never need regenerating and badges that stay accurate, in exchange for depending on a domain and a design that the README says may change. Downloading gives you a permanent artefact, at the cost of badges frozen at generation time. Self-hosting gives you permanence and control of the URL, at the cost of one container, one token and your own GitHub rate limit.

A third option exists for the common case, and the README mentions it: a separate CLI tool called `github-social-image` that uploads social images to all your repositories at once. If you have twenty repositories and you want a downloaded image everywhere, that is the tool for the job rather than thirty manual downloads. Nothing in this repository enforces the choice; it is a rendering service with a URL, and the operational question is who hosts it.

There is a Privacy section in the README that the browser-rendered summary cuts off mid-sentence, so what the service does and does not log on the hosted instance is not something to assume. On a self-hosted instance that question becomes yours to answer, and the `.env` file shows there are only three configuration variables to reason about.

## Conclusion

Socialify solves one narrow problem well: your repository has no screenshot, no logo wall and no visual hook, and you want something more deliberate than the default GitHub social preview. The live URL approach is the one to prefer, because the badge values in the image stay current without you regenerating anything, and a downloaded PNG is the fallback for places that require an uploaded file. The parts to think about before committing are the token and the SLA. A `GITHUB_TOKEN` with read-only access to public repositories is required for self-hosting, which means your instance is making authenticated GitHub calls and inherits GitHub's rate limits. And the README is explicit that the hosted service carries no service level agreement, that design and domain can change without notice, and that downloading the images or self-hosting is the answer if that matters to you. The self-host path is short: `docker-compose up -d`, a token in `.env`, and the instance is on port 3000.

## FAQ

### What is socialify.dev and how do I add one to my repository?

Socialify generates a designed image for a GitHub repository, served live from a URL so the badges update automatically. You link to `socialify.dev/owner/repo/image` with query parameters and embed it in a README with an `img` tag. You can also download the image as a PNG, JPEG or WebP and use it anywhere that requires an uploaded file.

### Do I need a GitHub login or token to self-host Socialify?

Self-hosting needs a GitHub token, not a login. The `.env.example` file describes `GITHUB_TOKEN` as a token with public repositories read-only access, created from the personal access tokens page, and the other two variables are `PROJECT_URL` for the base URL and an optional Google Tag Manager ID.

### How do I run Socialify with Docker?

The README gives two routes. Either `docker-compose up -d`, using the compose file in the repository which builds from the local context and maps port 3000, or build the image and run it directly with `docker build -t socialify .` followed by `docker run -p 3000:3000 --env-file .env socialify`. The final container image is distroless, so there is no shell inside it.

### Is there a service level agreement for the hosted Socialify service?

No. The README states that design and project domain are subject to change without notice, points to issue 47 for service updates, and suggests downloading the images or self-hosting if that is a problem. The repository is not archived and the last push was on 2026-09-14.

## Sources

- [License: MIT](https://github.com/wei/socialify/blob/master/LICENSE)
- [Project website](http://socialify.git.ci)
- [README](https://github.com/wei/socialify/blob/master/README.md)
- [Releases](https://github.com/wei/socialify/releases)
- [wei/socialify on GitHub](https://github.com/wei/socialify)

---

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