click2edit
v0.2.1
Published
Tiny inline editing for any JS stack (vanilla core + React adapter)
Maintainers
Readme
click2edit
Lightweight inline content editing for small websites:
click2edit-> framework-agnostic vanilla coreclick2edit/react-> optional React adapterclick2edit/server-> tiny Node server for global persistence (optional)
No admin panel. No CMS requirement. Pluggable storage.
Install
npm i click2editWhat You Get
- Click-to-edit text and list fields
- Edit mode protected by password (server-side auth supported)
- Keyboard toggle:
Cmd/Ctrl + Shift + E - Save on blur, cancel with
Esc - Storage adapters:
- local (
localStorage) - HTTP API
- Vercel API route helper
- custom adapter
- local (
Core API (Vanilla / Any JS Stack)
import { editable, vercelAdapter, vercelPasswordAuth } from "click2edit";
editable.init({
authorize: vercelPasswordAuth({ endpoint: "/api/editable" }),
sessionKey: "__editable_session__",
storageAdapter: vercelAdapter({
endpoint: "/api/editable",
}),
});
const unmount = editable.mount(document);
// unmount() when neededVanilla Markup
<h2 data-editable="pricing.basic" data-editable-default="199 zl">199 zl</h2>
<p
data-editable="hero.description"
data-editable-default="Fast edits without CMS"
data-editable-multiline="true"
>
Fast edits without CMS
</p>
<ul data-editable-list="pricing.features" data-editable-default="SEO|Hosting|Support">
<li>SEO</li>
<li>Hosting</li>
<li>Support</li>
</ul>React Adapter
import { editable, vercelAdapter, vercelPasswordAuth } from "click2edit";
import { EditableProvider, Editable, EditableList } from "click2edit/react";
editable.init({
authorize: vercelPasswordAuth({ endpoint: "/api/editable" }),
storageAdapter: vercelAdapter({
endpoint: "/api/editable",
}),
});
export function App() {
return (
<EditableProvider>
<Editable id="pricing.basic" defaultValue="199 zl" />
<Editable
id="hero.description"
defaultValue="Fast edits without CMS"
multiline
/>
<EditableList
id="pricing.features"
defaultValue={["SEO", "Hosting", "Support"]}
/>
</EditableProvider>
);
}Storage Adapters
import {
localStorageAdapter,
httpAdapter,
vercelAdapter,
httpPasswordAuth,
vercelPasswordAuth,
type StorageAdapter,
} from "click2edit";
const local = localStorageAdapter("__editable_content__");
const http = httpAdapter({ url: "https://example.com/api/editable", password: "secret123" });
const vercel = vercelAdapter({ endpoint: "/api/editable" });
const auth = httpPasswordAuth({ url: "https://example.com/api/editable" });
const vercelAuth = vercelPasswordAuth({ endpoint: "/api/editable" });
const custom: StorageAdapter = {
async load() { return {}; },
async save(content) { /* your backend save */ },
};editable.init(...) Options
password?: string-> client-side fallback password (visible in frontend bundle)authorize?: (password) => Promise<boolean> | boolean-> preferred server-side authstorageKey?: string-> local adapter key fallbacksessionKey?: string-> sessionStorage key for edit modestorageAdapter?: StorageAdapter-> custom source of truth
Optional Node Server (click2edit/server)
import { createEditableServer } from "click2edit/server";
createEditableServer({
port: 3001,
endpoint: "/__editable",
password: "secret123",
filePath: "editable-content.json",
corsOrigin: "*",
}).start();Then use:
import { editable, httpAdapter } from "click2edit";
editable.init({
password: "secret123",
storageAdapter: httpAdapter({
url: "http://localhost:3001/__editable",
password: "secret123",
}),
});Vercel Global Setup
Frontend:
- use
vercelAdapter({ endpoint: "/api/editable" }) - set
authorize: vercelPasswordAuth({ endpoint: "/api/editable" })
Backend:
api/editable.tsin this repo
Required env on Vercel:
EDITABLE_PASSWORDclick2edit_READ_WRITE_TOKENorBLOB_READ_WRITE_TOKEN- (optional)
EDITABLE_SESSION_SECRET
Local Demo
npm install
npm run demoPages:
- React demo:
http://localhost:5173/ - Vanilla demo:
http://localhost:5173/vanilla.html
Demo password:
- local pure Vite mode: any password (dev fallback)
- deployed mode: value from backend env
EDITABLE_PASSWORD
QA Checklist Before Release
- Build package:
npm run build- Open demo pages and verify:
- React demo:
http://localhost:5173/ - Vanilla demo:
http://localhost:5173/vanilla.html - toggle edit mode, edit text/list, blur save,
Esccancel - refresh page and verify persisted values
- If using global mode, verify API:
curl -i https://your-app.vercel.app/api/editable -H "x-editable-password: <password>"
curl -i -X PUT https://your-app.vercel.app/api/editable \
-H "content-type: application/json" \
-H "x-editable-password: <password>" \
-d '{"pricing.basic":"299 zl"}'Build / Publish
npm run build
npm publish --access publicRelease Flow (Git + npm)
# 1) review changes
git status
# 2) stage only project files (recommended explicit list)
git add src api demo README.md package.json package-lock.json tsconfig.build.json vercel.json server
# 3) commit
git commit -m "feat: universal core + react adapter + server-side auth"
# 4) push
git push
# 5) version bump (choose one)
npm version patch
# or npm version minor
# 6) publish
npm publish --access publicAfter publish:
- test in a clean sample app (
npm i click2edit@latest) - verify
import { editable } from "click2edit"andimport { Editable } from "click2edit/react"both work.
Troubleshooting
- Demo fails in old Node runtime
- Use Node
>=18(recommended20+) for modern Vite.
- Use Node
- Edits work only locally
- Ensure
storageAdapteris HTTP/Vercel-based and backend is reachable.
- Ensure
- Unauthorized on API
- Verify
EDITABLE_PASSWORDand request auth flow (authorizeor legacy header).
- Verify
- Vercel Blob overwrite error
- Ensure API uses
allowOverwrite: trueinput(...).
- Ensure API uses
Security Notes
This package is intentionally simple:
- if you use
passwordoption, it is frontend-visible by design - for hidden password use
authorizewith server-side validation - no users/roles
- no encryption
Use for non-sensitive marketing content.
