Open-source project
sambecker/exif-photo-blog avatar
sambecker/exif-photo-blog

sambecker/exif-photo-blog: a Next.js photo blog that keeps your camera data

Photo blog, reporting 🤓 EXIF camera details (aperture, shutter speed, ISO) for each image.

1,720 stars388 forksTypeScriptLicense varies

At a glance

What is it?
A self-hosted Next.js photo blog that reads EXIF from each upload and turns aperture, shutter speed and ISO into part of the page. It is built for photographers who want their own site and can live with Vercel as the deployment target.
Who is it for?
Adopt it if you shoot regularly, want the camera settings printed next to each frame, and are comfortable running a Next.js app on Vercel with Postgres and Blob storage. Do not adopt it if you need a host-agnostic CMS, a non-Next.js stack, or a project with a documented licence, because the repository material gives no licence identifier.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 19, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem: photo blogs that throw the camera data away

Most photo publishing tools treat EXIF as noise. They strip it during upload, downscale the file, and give you a caption box. For a photographer, that discards the most interesting part of the record: which lens, which aperture, which shutter speed, which ISO, and for Fujifilm shooters, which film simulation or recipe was applied.

sambecker/exif-photo-blog is aimed at the person who wants that metadata on the page rather than in a Lightroom sidecar. The README lists the feature as "Photo upload with EXIF extraction", and the repository topics include exif, photography and nextjs. The audience is narrow on purpose: someone with a camera, a domain, and enough patience to run a Next.js deployment. It is not a photo-sharing network and it is not a gallery plugin for an existing CMS. It is the whole site.

How the EXIF pipeline and storage layout fit together

The stack is a Next.js App Router application (the topics list approuter and nextjs) with Postgres for records and object storage for the image bytes. The README's deploy flow provisions Vercel Postgres and Vercel Blob together, and package.json carries both @vercel/blob and the S3 client packages @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner, which is what makes the alternate storage providers section possible.

EXIF parsing is not left to one library. package.json depends on exifr, ts-exif-parser and piexifjs at the same time. exifr and ts-exif-parser read metadata on the way in; piexifjs can write it, which is the plausible route for the Fujifilm recipe and film simulation support the README advertises. Colour handling is separate: extract-colors, fast-average-color and culori feed the blur placeholder and colour analysis, and sharp does the resizing.

The derived data then fans out. Photos are grouped by tag and by camera through routes the README names as tag/[tag] and shot-on/[make]/[model]. OG images are generated automatically, and the four static optimization flags exist precisely because those routes and images can be rendered at build time instead of per request. Search runs through a CMD-K menu (the cmdk dependency), and RSS and JSON feeds are generated from the same records. The data flow is one-directional: upload, extract, store, render.

Installing sambecker/exif-photo-blog and uploading a first photo

The README's installation path is the Vercel deploy button, not a git clone. The button provisions the project and, in the same flow, asks you to add Vercel Postgres and Vercel Blob. The README states that when creating the blob store you must configure it as public, and that Neon from the Vercel Marketplace is the preferred Postgres provider.

After the deploy, set the production domain variable in project settings. The README gives photos.domain.com as the example value, and names the variable NEXT_PUBLIC_DOMAIN, used in absolute urls and seen in navigation if no explicit nav title is set.

Auth comes next. The README points at a secret generator and names three variables, then asks you to redeploy from the Deployments tab: AUTH_SECRET, ADMIN_EMAIL and ADMIN_PASSWORD. The README does not print a shell snippet for them, so set them in the Vercel project's environment variables rather than pasting values into a file.

With the redeploy finished, visit /admin, sign in with the credentials from the previous step, click Upload Photos, add an optional title, and click Create. The EXIF block on the resulting photo page is the thing to check: if aperture, shutter speed and ISO appear, extraction worked end to end.

For local work the README gives a five-step sequence: clone the code, run pnpm i to install dependencies, install the Vercel CLI if necessary and authenticate by running vercel login, run vercel link to connect the CLI to your project, then run vercel dev to start the dev server with Vercel-managed environment variables.

bash
pnpm i
vercel login
vercel link
vercel dev

The package manager is pinned in package.json as pnpm@12.4.1, so pnpm is the expected tool rather than npm or yarn.

Where the local workflow and the storage model push back

The README's own FAQ asks whether you can work locally without access to an image storage provider, which is an admission that the answer is not a clean yes. Local development is routed through vercel dev so that environment variables come from the linked Vercel project. That means an offline laptop, or a machine where you have not run vercel login, is not the intended setup.

The second constraint is cost shape rather than a bug. The performance section carries an explicit warning that enabling the static optimization flags may result in increased project usage, and the README's FAQ keeps a troubleshooting entry for production deployments that fail when static optimization is on. So the flags that make photo pages fast at serve time move work into the build, and builds are metered. There is a matching knob for the opposite problem: NEXT_PUBLIC_DISABLE_BLUR prevents image blur data being stored and displayed, which the README frames as potentially useful for limiting Postgres usage.

Third, the storage layer is not fully portable today. The alternate database providers section is labelled experimental in the README's own table of contents, while alternate storage providers is not. If you want to run this against MinIO or R2, the object storage side has a documented route; the database side is the part to treat with caution.

The wrong tool test is simple. If you want a static site generator that emits HTML you can host on any bucket, this is not that. It needs a running Node server, a Postgres instance and an object store.

The AI descriptions and what they actually require

The README lists AI-generated text descriptions as a feature, and package.json backs that with the ai package plus @ai-sdk/openai and @ai-sdk/rsc. Configuration is provider-first. Vercel AI Gateway is described as the recommended path, with direct OpenAI or an OpenAI-compatible endpoint as the alternate, and the README states that if both variables are set, OPENAI_SECRET_KEY takes precedence.

For the gateway route you set AI_GATEWAY_MODEL in creator/model-name form. The README's example is openai/gpt-5.2, and it states the model must support image input (vision). On Vercel, authentication happens automatically through OIDC, so no key is needed; outside Vercel, or for local development without vercel env pull, you generate a key from the Vercel AI Gateway dashboard and store it in AI_GATEWAY_API_KEY. The README also recommends adding rate limiting, which is what the @upstash/ratelimit and @upstash/redis dependencies are for.

This is optional. A blog with EXIF and no AI text works; you just skip the provider configuration. The README does not document what happens to already-uploaded photos if you enable a provider later.

ChronoFrame, PicImpact and the static-site alternative

ChronoFrame and PicImpact appear in the related searches as the projects people compare this against. The structural difference is where the EXIF rendering happens. This project is a server-rendered Next.js application that reads from Postgres and object storage per request (or at build time when the static flags are on), which is what lets it generate OG images, category pages and a search index from the same records.

A static generator takes the opposite route: you build locally, the output is files, and hosting is a bucket or a CDN with no database. That is cheaper and more portable, and it is why someone would choose it over this. The trade is that upload, auth and on-demand OG image generation do not exist unless you rebuild and redeploy each time. If your publishing rhythm is a batch of photos once a month, the static route is a reasonable answer. If you upload from a phone and expect the page to appear, this project's admin flow at /admin is the reason to pick it.

A third option is a general-purpose CMS with a gallery plugin. You get a familiar editor, but the EXIF display is whatever the plugin author decided, and the shot-on/[make]/[model] grouping this project builds is unlikely to exist.

Updates, licence and the cost of staying current

The README addresses upgrades directly. It suggests forking the repository if you do not plan to change the code, or do not mind making your updates public, so that you can receive future updates; it also links migration instructions for projects already on Vercel under a "how do I receive template updates" FAQ entry. That is a real fork-or-follow decision, and the wording implies the maintainer's expectation is a fork rather than a long-lived divergent branch.

The repository material does not state a licence. The package.json excerpt is truncated and the README has no licence section, so there is no identifier to check against. If you plan to redistribute the code, sell a hosted version, or ship it inside a client project, resolve that question first; the absence of a stated licence is a reason to ask rather than assume permissive terms. Nothing here is legal advice.

Upgrade cost is dominated by the framework, not the app. package.json pins next to 16.3.5, react to 19.3.0, next-auth to 5.0.0-beta.32 and the AWS SDK packages to 3.1131.0. A beta auth dependency is the one to watch: next-auth 5.0.0-beta.32 means auth behaviour can change between releases, and auth is the part of this project you cannot skip. The last push to the repository was on 2026-09-15.

Editorial conclusion

Adopt it if you shoot regularly, want the camera settings printed next to each frame, and are comfortable running a Next.js app on Vercel with Postgres and Blob storage. Do not adopt it if you need a host-agnostic CMS, a non-Next.js stack, or a project with a documented licence, because the repository material gives no licence identifier. Before you commit, verify three things: that your Vercel Blob store is created as public, that NEXT_PUBLIC_DOMAIN matches the domain you actually serve, and whether you will leave the four NEXT_PUBLIC_STATICALLY_OPTIMIZE_* flags off, since the README warns they raise project usage.

Frequently asked questions

What is sambecker/exif-photo-blog?

It is a Next.js photo blog that extracts EXIF camera details from each uploaded image and displays them on the photo page. The README lists built-in auth, tag organisation, infinite scroll, CMD-K search, RSS and JSON feeds, and Fujifilm recipe support.

How do I install sambecker/exif-photo-blog?

The README's path is the Vercel deploy button, which provisions the project and asks you to add Vercel Postgres and Vercel Blob in the same flow. You then set NEXT_PUBLIC_DOMAIN, add AUTH_SECRET, ADMIN_EMAIL and ADMIN_PASSWORD, and trigger a redeploy.

Can I run sambecker/exif-photo-blog locally without Vercel storage?

The README's own FAQ addresses local development without access to an image storage provider, and the documented local flow runs through vercel dev after vercel link so the dev server picks up Vercel-managed environment variables. The README does not describe a fully self-contained offline mode.

Does sambecker/exif-photo-blog need an AI provider to work?

No. AI-generated text descriptions and colour analysis are optional and require configuring either Vercel AI Gateway or an OpenAI-compatible endpoint. The README states that if both are set, OPENAI_SECRET_KEY takes precedence.

What storage and database does sambecker/exif-photo-blog use?

The default deploy uses Vercel Postgres with Neon preferred, plus Vercel Blob, which the README says must be created as public. Alternate storage providers are documented, while alternate database providers are marked experimental in the README's contents.

Official sources

  1. Issues
  2. Project website
  3. README
  4. sambecker/exif-photo-blog on GitHub
Community notes

Community notes