@manufosela/firebase-autoform

@manufosela/firebase-autoform

Lit 3 web component for auto-generated forms from Firebase database schema

Firebase Autoform

Generate a full form from a schema definition. Submits to Firebase when configured, and always emits events for local workflows.

Auto-generated form

Event log

Schema fields: text, email, number, select, checkbox, textarea, and grouped sections.
Demo code (CodePen-ready HTML, CSS, JS)
HTML (html)
<h1>Firebase Autoform</h1>
    <p class="description">
      Generate a full form from a schema definition. Submits to Firebase when
      configured, and always emits events for local workflows.
    </p>

    <div class="layout">
      <div class="panel">
        <h2>Auto-generated form</h2>
        <firebase-autoform id="autoform"></firebase-autoform>
      </div>

      <div class="panel">
        <h2>Event log</h2>
        <div class="event-log" id="eventLog"></div>
        <div class="schema-note" style="margin-top: 1.5rem;">
          Schema fields: text, email, number, select, checkbox, textarea, and grouped sections.
        </div>
      </div>
    </div>
CSS (css)
:root {
  --bg: #0c0f14;
  --bg-elevated: #141923;
  --bg-panel: #171d28;
  --border: #262f3f;
  --text: #f4f6fb;
  --text-muted: #a7b0c2;
  --text-dim: #7d879b;
  --accent: #ff8a3d;
  --accent-strong: #ff6a00;
  --accent-soft: rgba(255, 138, 61, 0.16);
  --shadow: 0 20px 50px rgba(5, 8, 14, 0.45);
  --radius-lg: 22px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --max-width: 1160px;
}

h1 {
        color: var(--accent);
        margin-bottom: 0.5rem;
      }

      .description {
        color: var(--text-muted);
        margin-bottom: 2rem;
      }

      .layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(260px, 360px);
        gap: 2rem;
        align-items: start;
      }

      .panel {
        background: var(--bg-elevated);
        border: 1px solid var(--border);
        border-radius: 12px;
        padding: 1.5rem;
        box-shadow: 0 18px 40px rgba(0, 0, 0, 0.2);
      }

      .panel h2 {
        margin: 0 0 1rem;
        color: var(--accent);
        font-size: 1.1rem;
      }

      .event-log {
        background: var(--bg-panel);
        padding: 1rem;
        border-radius: 10px;
        min-height: 200px;
        max-height: 320px;
        overflow-y: auto;
        font-family: "JetBrains Mono", ui-monospace, monospace;
        font-size: 0.85rem;
        color: var(--text);
      }

      .event-log p {
        margin: 0 0 0.75rem;
        padding-bottom: 0.75rem;
        border-bottom: 1px dashed var(--border);
      }

      .event-log p:last-child {
        border-bottom: none;
        margin-bottom: 0;
        padding-bottom: 0;
      }

      .schema-note {
        background: var(--bg-panel);
        border-radius: 10px;
        padding: 1rem;
        color: var(--text-muted);
        font-size: 0.85rem;
      }

      @media (max-width: 900px) {
        .layout {
          grid-template-columns: 1fr;
        }
      }
JS (js)
import "https://esm.sh/@manufosela/firebase-autoform";

      const form = document.getElementById('autoform');
      const eventLog = document.getElementById('eventLog');

      form.schema = {
        name: { type: 'text', label: 'Name', required: true, placeholder: 'Ada Lovelace' },
        email: { type: 'email', label: 'Email', required: true, placeholder: 'ada@domain.com' },
        age: { type: 'number', label: 'Age', min: 18, max: 99, group: 'Profile' },
        role: {
          type: 'select',
          label: 'Role',
          options: [
            { value: 'admin', label: 'Admin' },
            { value: 'editor', label: 'Editor' },
            { value: 'viewer', label: 'Viewer' }
          ],
          group: 'Profile'
        },
        subscribed: { type: 'checkbox', label: 'Newsletter opt-in', group: 'Preferences' },
        notes: { type: 'textarea', label: 'Notes', placeholder: 'Optional notes', group: 'Preferences' }
      };

      form.path = '/users';
      form.submitLabel = 'Save profile';
      form.resetLabel = 'Reset';
      form.showSuccess = true;
      form.successMessage = 'Saved locally. Ready for Firebase!';

      const log = (label, detail) => {
        const entry = document.createElement('p');
        entry.textContent = `${new Date().toLocaleTimeString()} - ${label}: ${JSON.stringify(detail)}`;
        eventLog.prepend(entry);
      };

      form.addEventListener('form-submit', (event) => log('submit', event.detail));
      form.addEventListener('form-error', (event) => log('error', event.detail));
      form.addEventListener('form-reset', () => log('reset', {}));