Open-source project
markfulton/NanoBananaEditor avatar
markfulton/NanoBananaEditor

NanoBananaEditor and the credit-metered Gemini image editor you can host

The most advanced Nano Banana image generator and editor application. Your central hub for AI image generation and revisions. Intuitive UI features reference images, editing with image masks, version history, and more. Powered by Gemini Flash images API.

726 stars167 forksTypeScriptAGPL-3.0

At a glance

What is it?
NanoBananaEditor is a React and TypeScript front end for Google's Gemini image models, packaged so it can be self-hosted on Supabase with its own credit accounting, Stripe checkout and edge functions. The credit ledger and the model catalog are the parts that decide whether it fits your use.
Who is it for?
NanoBananaEditor fits a team that wants a multi-model Gemini image editor with masks, grounded search and a credit ledger it controls, and that is prepared to run Supabase edge functions and own a Gemini API key. It does not fit a single user who only needs a few images and would rather not run Postgres row level security, an edge function and a webhook.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

Editorial analysis

A browser editor for three Gemini image models

The project is a React and TypeScript application for Google's Gemini image models. The description of the workflow is concrete: write a prompt, drop in reference photos, paint a mask, ground the prompt in live search results, and render from a 512px draft to a 4K final. Every render lands in a local history you can branch from.

Three models sit behind one interface, and the choice is a table of trade-offs rather than a preference. Nano Banana 2 Lite has the model id gemini-3.1-flash-lite-image, outputs 1K, takes up to 14 inputs, has no search grounding and offers fast or deep thinking. Nano Banana 2 is gemini-3.1-flash-image, outputs 512, 1K, 2K or 4K, takes up to 10 inputs, supports search grounding and offers fast or deep thinking. Nano Banana Pro is gemini-3-pro-image, outputs 1K, 2K or 4K, takes up to 6 inputs, supports search grounding and always has thinking on.

Aspect ratios are fixed in code, from 1:1 through 16:9, 9:16 and 21:9, with 4:1, 1:4, 8:1 and 1:8 available on the two Flash models only.

The feature list is where this stops being a prompt box. Editing carries the last turns as context, a brush and eraser keep a mask local, a compare slider shows before and after, a one-click 4K re-render uses Nano Banana Pro, and history survives a refresh because it is persisted to IndexedDB with per-item settings, model notes, grounding sources and request details.

One catalog of prices, copied so the client cannot disagree

The billing model is the part of the system that deserves attention, and it starts with a single source of truth. One catalog, src/lib/models.ts, defines what each model can do and what it costs. The edge function keeps an identical copy, and the stated reason is that the client can never lie about a price.

The costs are counted in credits rather than money. Nano Banana 2 Lite costs one credit for a 1K output and nothing for the other sizes. Nano Banana 2 costs one credit at 512 and 1K, two at 2K and three at 4K. Nano Banana Pro costs three at 1K, three at 2K and five at 4K. Search grounding adds one credit to any request.

Keeping the copy in sync is an explicit chore, and the project gives you a script for it. The sync:models command copies the model catalog into the edge function after you edit it, and the package.json shows it running node scripts/sync-models.mjs. If you forget to run it after changing a price, the client and the server disagree about the cost, which is exactly the failure the duplicate copy was meant to prevent.

The payment side, when you want it, is Stripe. The scripts and the migrations cover checkout and a webhook, and the deployment step sets STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET as edge function secrets.

Credits are reserved before the model runs and refunded when it fails

The credit flow has a specific order of operations, and the order is the interesting part. New accounts start with 5 free credits, granted by a database trigger on signup rather than by application code. Credits are reserved server-side before the model runs, and refunded automatically if the render fails or comes back empty.

That sequence closes a specific failure mode. Without reservation you either charge for renders that never happened, or you discover the failure after the model has already billed you. With reservation first and refund on an empty or failed result, the balance reflects what the user actually received.

The implementation is in the database rather than in the edge function. The migrations in supabase/migrations create the credits table, the Stripe tables, the usage log and the server-only credit functions. The security notes describe the shape: credits are deducted with a SECURITY DEFINER function that only the service role can execute, and users can read their balance and nothing else.

The whole accounting layer therefore depends on Postgres row level security being configured correctly in your own project, which is the part that makes self-hosting more than a copy of the repository.

Self-hosting needs Supabase, an edge function and a key in the right place

The prerequisites are listed plainly: Node 18 or newer, a Supabase project, a Gemini API key from Google AI Studio, and a Stripe account if you intend to sell credits.

Clone and install:

bash
git clone https://github.com/markfulton/NanoBananaEditor.git
cd NanoBananaEditor
npm install
cp .env.example .env

The env file only holds public values, and the example says so:

bash
VITE_SUPABASE_URL=https://YOUR-PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

Both ship in the browser bundle, so they are not secrets. The server-side key never belongs there, as the same file states, since the Gemini key lives in Supabase secrets.

Apply the migrations in supabase/migrations in order, either with the Supabase CLI command supabase db push or by pasting them into the SQL editor, then set the secrets and deploy the functions:

bash
supabase secrets set GEMINI_API_KEY=your-key
supabase secrets set STRIPE_SECRET_KEY=sk_... STRIPE_WEBHOOK_SECRET=whsec_...
supabase functions deploy nano-image
supabase functions deploy stripe-checkout
supabase functions deploy stripe-webhook --no-verify-jwt

Then start the Vite dev server:

bash
npm run dev

Open http://localhost:5173, sign up, confirm the email, and spend the 5 starter credits. After that you either buy credits through Stripe or paste your own Gemini key in Settings.

Bring your own key, and the boundary it draws around the house key

There are two ways to pay for a render, and the difference matters for anyone hosting this for other people.

The first is the house key path. The Gemini key lives in Supabase secrets, and @google/genai runs only inside the edge function, so the key never reaches the browser. The edge function also requires a signed-in, email-confirmed user, and the notes are explicit that the anon key alone cannot generate. Anyone hitting the function with the public key gets nothing.

The second is the bring your own key path. You paste a Gemini API key from AI Studio in Settings, the key is stored only in your browser, and requests bill your Google account directly. It is sent per request over HTTPS.

That option removes the credit ledger from the picture for that user, which makes it the sensible choice for a solo operator or a private deployment. It also means the cost control the rest of the system is built around no longer applies, since every render is billed by Google to that account.

The wider security posture is worth reading in the source. Credits are deducted through a SECURITY DEFINER function that only the service role can execute, and users can read their balance and nothing else. The edge function is the only place a privileged credential lives.

History in IndexedDB, and what that means for the browser boundary

The state layer is split across three stores, and the split has consequences. useAppStore holds the composer, canvas and history state, persisted to IndexedDB. useThemeStore handles the light, dark and system choice. useSettingsStore holds your own API key and the download format. The dependency list includes idb-keyval, which is the IndexedDB wrapper in use, and zustand for the stores themselves.

Persisting history to IndexedDB means renders survive a refresh and can be branched from later, with per-item settings, model notes, grounding sources and request details attached to each entry. It also means the images live in the browser, not on a server you control, which suits a local-first feel and avoids you becoming the host of everyone's pictures.

The masks are drawn with Konva and react-konva, with the brush and eraser sizes adjusted from the keyboard, and a service in the source tree turns brush strokes into a mask plus a tinted preview. Follow-up edits carry the last turns as context, which is why the canvas and history state are shared through the store rather than kept per-component.

The stack list in the documentation is React 18, TypeScript, Tailwind, Zustand, TanStack Query, Konva, Radix and Vite, with Supabase for auth, Postgres and edge functions.

A hosted instance, a direct API script, and the AGPL-3.0 question

There is a live instance of the app at nanobananaeditor.dev, and there is also the one-click Bolt.new install offered through the Vibe Coding is Life community, alongside project downloads and live build sessions. The alternative to self-hosting is therefore the hosted editor, and the trade-off is straightforward: you get the full interface with no Supabase project, and you give up control of the key, the credits and the data.

The other alternative is not an editor at all. You can call the Gemini image models from your own script with @google/genai and skip the credit ledger, the Stripe tables, the edge function and the row level security. What you lose is the parts that make image work iterative: the mask pipeline, the multi-turn context, the branchable history and the price catalog. If you need one image from a prompt, that script is less machinery. If you need a queue of masked revisions with per-item notes, the editor has already built the awkward parts.

On the paperwork, the licence is AGPL-3.0, which is a stronger copyleft condition than MIT or Apache for a network application, so a hosted or modified deployment of this code is the case where you need to read the licence text rather than skim it. The last push was on 2026-10-07, the package version is 2.1.0, and the repository has no GitHub releases, though a CHANGELOG.md is tracked in the root alongside a docs directory that holds the self-hosting guide and the architecture note.

The full walkthrough for Stripe, custom SMTP and troubleshooting lives in docs/self-hosting.md, and the request flow and the reasoning behind masks and multi-turn edits are in docs/architecture.md.

Editorial conclusion

NanoBananaEditor fits a team that wants a multi-model Gemini image editor with masks, grounded search and a credit ledger it controls, and that is prepared to run Supabase edge functions and own a Gemini API key. It does not fit a single user who only needs a few images and would rather not run Postgres row level security, an edge function and a webhook. Before adopting, read the migrations in supabase/migrations and the security notes about the SECURITY DEFINER credit function, confirm the credit costs in src/lib/models.ts match what you plan to charge, and decide whether the AGPL-3.0 licence suits how you intend to distribute the app.

Frequently asked questions

What do I need to run NanoBananaEditor locally?

Node 18 or newer, a Supabase project, a Gemini API key from Google AI Studio, and a Stripe account only if you want to sell credits. You also apply the migrations in supabase/migrations and deploy the nano-image edge function with the key set as a Supabase secret.

How much do the credits cost in NanoBananaEditor?

The catalog in src/lib/models.ts sets the prices: Nano Banana 2 Lite costs 1 credit for 1K output, Nano Banana 2 costs 1 credit at 512 and 1K and 3 at 4K, and Nano Banana Pro costs 3 at 1K and 5 at 4K. Search grounding adds 1 credit, and new accounts start with 5 free credits granted on signup.

Can I use my own Gemini API key in NanoBananaEditor?

Yes. Paste a key from AI Studio in Settings and it is stored only in your browser, sent per request over HTTPS, and the renders bill your Google account directly. The key held by the app for paying users stays in Supabase secrets and only the edge function can use it.

Which models does NanoBananaEditor support?

Three: Nano Banana 2 Lite with the id gemini-3.1-flash-lite-image, Nano Banana 2 with gemini-3.1-flash-image, and Nano Banana Pro with gemini-3-pro-image. Only the two Flash models support the extra wide aspect ratios 4:1, 1:4, 8:1 and 1:8.

Does NanoBananaEditor keep my generated images on a server?

The history is persisted to IndexedDB in the browser, so renders survive a refresh and can be branched from. The Gemini key used by the hosted service is kept server-side in Supabase secrets and never reaches the browser.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. markfulton/NanoBananaEditor on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/markfulton-nanobananaeditor.svg)](https://hysenlabs.com/projects/markfulton-nanobananaeditor)

Was this useful?