Settings UI Integration

If your plugin needs configuration from the blog owner — an API key, a list ID, a webhook URL — you build a React settings panel and declare it in the manifest. VeloCMS renders it inside the admin dashboard at /admin/plugins/[your-plugin]/settings. The owner fills it in, hits save, and your stored config is available to the handler via ctx.settings at hook invocation time.

Declaring the admin entry point

In manifest.json, add the admin key inside entry:

{
  "entry": {
    "runtime": "./dist/runtime.js",
    "admin": "./dist/admin.js"
  }
}

The admin component contract

Your default export must be a React component. VeloCMS injects two props: initialSettings (the currently saved config, decrypted) and onSave (an async callback that persists the updated config). The component is loaded client-side only — you can use React state freely.

"use client";

interface Props {
  initialSettings?: { api_key?: string };
  onSave?: (settings: { api_key: string }) => Promise<void>;
}

export default function MyPluginSettings({ initialSettings = {}, onSave }: Props) {
  const [apiKey, setApiKey] = useState(initialSettings.api_key ?? "");
  const [saving, setSaving] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);
    await onSave?.({ api_key: apiKey });
    setSaving(false);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        API Key
        <input
          type="password"
          value={apiKey}
          onChange={(e) => setApiKey(e.target.value)}
          required
        />
      </label>
      <button type="submit" disabled={saving}>
        {saving ? "Saving…" : "Save"}
      </button>
    </form>
  );
}

Encrypted storage for secrets

Any field your admin component passes to onSaveis stored in VeloCMS's plugin settings table. Fields whose key ends in _key, _secret, or _token are automatically encrypted with AES-256-GCM before storage. When VeloCMS calls your hook handler, it decrypts those values before injecting them into ctx.settings. Your handler always sees plaintext — the encryption layer is transparent.

Never log ctx.settings values that contain credentials. The observability pipeline does pattern-matching to catch accidental logging, but it is not a guarantee. Follow the principle: log that an action succeeded or failed, not what the secret was.

Styling guidelines

Do not import Tailwind directly into your admin component. VeloCMS applies its own admin theme (semantic tokens, not hardcoded colors) to everything inside the settings panel. Use plain HTML form elements and let VeloCMS style them. If you want custom layout, use inline style attributes with values that degrade gracefully — flex gaps, margins. Avoid pixel-perfect designs that might clash with admin theme updates.