KirokuForms

Developer Code Examples

Explore practical examples of integrating KirokuForms into your projects. From basic HTML to advanced API usage and custom styling, find the patterns you need.

Standard HTML Embed (Recommended)

The fastest way to integrate. It renders semantic HTML suitable for standard websites, blogs, and landing pages.

HTML Embed
<script src="https://www.kirokuforms.com/embed.js" 
  data-kiroku-form-id="YOUR_FORM_ID" 
  async></script>

React Component Embed

Renders your form dynamically on the client with a small bundled runtime (~12KB gzipped, Preact under the hood). Ideal for single-page apps (SPAs) that build their content in the browser.

React Mode Embed
<div id="form-root"></div>
<script src="https://www.kirokuforms.com/embed.js" 
  data-kiroku-form-id="YOUR_FORM_ID" 
  data-kiroku-target="#form-root"
  data-kiroku-mode="react"
  async></script>

Multiple Forms on One Page

Embed as many forms as you want on a single page. Each form renders into its own container, keeps its own state, and submits to its own form ID, so they never interfere with each other. The embed script is fetched once (the browser caches it) and initializes every form exactly once.

One script tag per form

The simplest approach. Give each form its own target container so it renders exactly where you place it:

Two forms, two script tags
<div id="signup"></div>
<script src="https://www.kirokuforms.com/embed.js"
  data-kiroku-form-id="FORM_A"
  data-kiroku-target="#signup"
  async></script>

<div id="contact"></div>
<script src="https://www.kirokuforms.com/embed.js"
  data-kiroku-form-id="FORM_B"
  data-kiroku-target="#contact"
  async></script>

Load once, initialize many

For single-page apps or pages with many forms, load the script once and call KirokuEmbed.init() for each form. The script and its runtime parse a single time, then each form renders into its own container:

One script, many forms
<script src="https://www.kirokuforms.com/embed.js" async></script>

<div id="a"></div>
<div id="b"></div>

<script>KirokuEmbed.init({ formId: 'FORM_A', target: '#a' });
  KirokuEmbed.init({ formId: 'FORM_B', target: '#b', mode: 'react' });
</script>
Give every form its own target. Pointing two forms at the same container makes the second one replace the first.

Open a HITL Task in a Modal

A human-in-the-loop (HITL) task form is scoped to a task id and a token. The page at /hitl/task/TASK_ID?token=TASK_TOKEN reads the token from the URL and validates it before it renders the task. To show that task in a modal on your own dashboard, point a same-origin iframe at the same URL. The token stays in the query string, so the task submits through its own page exactly as it does full screen.

HITL task page in an iframe
<iframe
  src="/hitl/task/TASK_ID?token=TASK_TOKEN"
  title="HITL task"
  style="width:100%;height:min(80vh,640px);border:none"
></iframe>

This iframe wrapper is the shipped path today. Our app drops that iframe into the same accessible popup modal used above (the openTaskModal helper), so focus trapping and reduced-motion handling carry over. It is presentation only: it issues no fetch, adds no auth header, and calls no API endpoint.

Future option: window.KirokuForms.openTask(taskId, token)

A token-aware window.KirokuForms.openTask(taskId, token) on the embed is a possible future addition. It would be additive and would carry the token in the URL, the same way the iframe does. It would not change /api/submissions/task or /api/mcp/*: those endpoints keep their current field names, structure, and Bearer/body-token auth, which external clients depend on. This method does not exist yet; use the iframe wrapper above.

Embed Customization

Our embed script is highly configurable. You can control rendering modes, target specific containers, and override styles to match your brand perfectly.

Looking for the full reference?

The most up-to-date list of configuration attributes, CSS variables, and advanced options is available directly in your dashboard.

Go to Dashboard → Select Form → Embed Tab

Quick Theming Example

Override our default CSS variables in your own stylesheet to instantly theme standard embeds:

CSS Overrides
/* Example: Customizing KirokuForms Embed with CSS Variables */
/* This applies if you are using a KirokuForms embed that respects these variables. */
/* Place this in your global CSS or a <style> tag on your page. */

/* :root or a specific wrapper for the KirokuForms embed */
.kiroku-form-custom-theme {
  --kiroku-color-primary: #F97316; /* Human Orange */
  --kiroku-color-text: #334155;    /* Kiroku Slate */
  --kiroku-color-border: #9ca3af;  /* Gray-400 */
  --kiroku-color-background: #F8FAFC; /* Kiroku White */
  --kiroku-border-radius: 0.5rem;  /* 8px */
  --kiroku-font-family: 'Georgia', serif; /* Example custom font */
  --kiroku-font-size: 1.1rem;

  /* Button specific overrides */
  --kiroku-button-text-color: white;
  --kiroku-button-hover-bg-color: #fb923c; /* Lighter orange */
}

HTML Form with Various Field Types

This example demonstrates an HTML form using different input types. Remember to include JavaScript for submission to the /api/forms/YOUR_FORM_ID/submit endpoint with your API key, similar to the Quick Start guide.

HTML
<form id="allFieldsForm">
  <div>
    <label for="fullName">Full Name:</label>
    <input type="text" id="fullName" name="fullName" required />
  </div>

  <div>
    <label for="contactEmail">Email Address:</label>
    <input type="email" id="contactEmail" name="contactEmail" required />
  </div>

  <div>
    <label for="quantity">Quantity:</label>
    <input type="number" id="quantity" name="quantity" min="1" max="100" />
  </div>

  <div>
    <label for="comments">Comments:</label>
    <textarea id="comments" name="comments" rows="5"></textarea>
  </div>

  <div>
    <label for="serviceType">Service Type:</label>
    <select id="serviceType" name="serviceType">
      <option value="">--Please choose an option--</option>
      <option value="support">Support</option>
      <option value="sales">Sales</option>
      <option value="feedback">Feedback</option>
    </select>
  </div>

  <div>
    <p>Preferred Contact Method:</p>
    <input type="radio" id="contactEmailRadio" name="contactMethod" value="email" checked />
    <label for="contactEmailRadio">Email</label><br/>
    <input type="radio" id="contactPhoneRadio" name="contactMethod" value="phone" />
    <label for="contactPhoneRadio">Phone</label>
  </div>

  <div>
    <p>Interests:</p>
    <input type="checkbox" id="interestTech" name="interests" value="technology" />
    <label for="interestTech">Technology</label><br/>
    <input type="checkbox" id="interestDesign" name="interests" value="design" />
    <label for="interestDesign">Design</label>
  </div>

  <div>
    <label for="appointmentDate">Appointment Date:</label>
    <input type="date" id="appointmentDate" name="appointmentDate" />
  </div>

  <button type="submit">Submit Detailed Form</button>
</form>
<script>
  // Basic JS submission logic (similar to quickstart) would go here,
  // targeting "allFieldsForm" and posting to /api/forms/YOUR_FORM_ID/submit
  // with an API Key.
</script>

Note: For brevity, the JavaScript submission logic is omitted here but would be required as shown in the Quick Start Guide.

Styling: Complete Freestyle (Custom HTML & CSS)

When you integrate using your own HTML structure and submit via our API, you have 100% control over the styling. KirokuForms injects no CSS. Use your own stylesheets, utility classes (like Tailwind CSS), or any method you prefer.

HTML Structure:

HTML
<form id="freestyleForm" class="my-custom-form">
  <div class="form-group">
    <label for="customName" class="form-label">Your Name:</label>
    <input type="text" id="customName" name="name" class="form-input" required />
  </div>
  <button type="submit" class="btn-submit-custom">Send It!</button>
</form>

Custom CSS:

CSS
/* Your custom CSS - style it exactly how you want! */
.my-custom-form {
  background-color: #f0f4f8;
  padding: 2rem;
  border-radius: 10px;
  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.my-custom-form .form-group {
  margin-bottom: 1.5rem;
}
.my-custom-form .form-label {
  display: block;
  color: #2c3e50;
  margin-bottom: 0.5rem;
  font-weight: bold;
}
.my-custom-form .form-input {
  width: 100%;
  padding: 0.75rem;
  border: 2px solid #bdc3c7;
  border-radius: 5px;
  transition: border-color 0.3s;
}
.my-custom-form .form-input:focus {
  border-color: #3498db; /* Kiroku Blue-like */
  outline: none;
}
.my-custom-form .btn-submit-custom {
  background-color: #2563EB; /* Kiroku Blue */
  color: white;
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
  transition: background-color 0.3s;
}
.my-custom-form .btn-submit-custom:hover {
  background-color: #1D4ED8; /* Darker Kiroku Blue */
}

Remember to add JavaScript submission logic to post to `/api/forms/YOUR_FORM_ID/submit` with your API key.

Manual Fetch API Submission

This example expands on the Quick Start's JavaScript submission. It demonstrates a more structured approach to using the Fetch API for client-side submissions, including handling file uploads (if your form has them) and providing more detailed user feedback.

The code below is generated by our generateFetchExampleSet utility. You would typically use this within your form's "Share" or "Embed" modal in the KirokuForms dashboard.

HTML Structure:

HTML
<form id="kirokuCustomFetchForm-examplePage">
  <h3>Example Fetch API Form</h3>
  <div id="kirokuCustomFetchForm-examplePage-messages" style="display:none;"></div>
  <div>
    <label for="fetchName">Name:</label>
    <input type="text" name="name" id="fetchName" required />
  </div>
  <div>
    <label for="fetchEmail">Email:</label>
    <input type="email" name="email" id="fetchEmail" required />
  </div>
  <button type="submit">Submit with Fetch</button>
</form>

JavaScript Logic:

JavaScript
// JavaScript for Fetch API Example (generated by customIntegrationExamples.ts)
// Remember to replace YOUR_FORM_ID and YOUR_API_KEY
document.addEventListener('DOMContentLoaded', () => {
  const formElement = document.getElementById('kirokuCustomFetchForm-examplePage');
  const messagesDiv = document.getElementById('kirokuCustomFetchForm-examplePage-messages');
  
  if (!formElement || !messagesDiv) {
    console.error('Form or messages element not found');
    return;
  }

  formElement.addEventListener('submit', async (event) => {
    event.preventDefault();
    
    // Show loading state
    messagesDiv.style.display = 'block';
    messagesDiv.innerHTML = '<p style="color: #2563EB;">Submitting...</p>';
    
    try {
      // Collect form data
      const formData = new FormData(formElement);
      const data = Object.fromEntries(formData.entries());
      
      // Submit to KirokuForms API
      const response = await fetch('/api/forms/YOUR_FORM_ID/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer YOUR_API_KEY',
          'Accept': 'application/json'
        },
        body: JSON.stringify(data)
      });
      
      const result = await response.json();
      
      if (response.ok) {
        messagesDiv.innerHTML = '<p style="color: #10b981;">✓ Form submitted successfully!</p>';
        formElement.reset();
      } else {
        messagesDiv.innerHTML = `<p style="color: #ef4444;">Error: ${result.error?.message || 'Submission failed'}</p>`;
      }
    } catch (error) {
      console.error('Submission error:', error);
      messagesDiv.innerHTML = '<p style="color: #ef4444;">Network error. Please try again.</p>';
    }
  });
  
  console.log("Fetch example JS loaded for kirokuCustomFetchForm-examplePage.");
});

Manual React Integration

Integrate KirokuForms into your React applications. This example shows a basic React component for form handling and submission to our API.

This code is generated by our generateReactExampleCode utility, often found in the form's "Share" or "Embed" modal.

JSX (React)
// React Component Example (generated by customIntegrationExamples.ts)
// Remember to replace YOUR_FORM_ID and YOUR_API_KEY
import React, { useState } from 'react';

interface FormData {
  name: string;
  email: string;
  message: string;
}

interface SubmissionStatus {
  type: 'idle' | 'loading' | 'success' | 'error';
  message?: string;
}

function KirokuFormReactExample() {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
    message: ''
  });
  
  const [status, setStatus] = useState<SubmissionStatus>({ type: 'idle' });

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setStatus({ type: 'loading' });

    try {
      const response = await fetch('/api/forms/YOUR_FORM_ID/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer YOUR_API_KEY',
          'Accept': 'application/json'
        },
        body: JSON.stringify(formData)
      });

      const result = await response.json();

      if (response.ok) {
        setStatus({ 
          type: 'success', 
          message: 'Form submitted successfully!' 
        });
        setFormData({ name: '', email: '', message: '' });
      } else {
        setStatus({ 
          type: 'error', 
          message: result.error?.message || 'Submission failed' 
        });
      }
    } catch (error) {
      setStatus({ 
        type: 'error', 
        message: 'Network error. Please try again.' 
      });
    }
  };

  return (
    <div className="max-w-md mx-auto">
      <form onSubmit={handleSubmit} className="space-y-4">
        <h3 className="text-lg font-semibold">React Form Example</h3>
        
        <div>
          <label htmlFor="name" className="block text-sm font-medium">
            Name:
          </label>
          <input
            type="text"
            id="name"
            name="name"
            value={formData.name}
            onChange={handleInputChange}
            required
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
          />
        </div>

        <div>
          <label htmlFor="email" className="block text-sm font-medium">
            Email:
          </label>
          <input
            type="email"
            id="email"
            name="email"
            value={formData.email}
            onChange={handleInputChange}
            required
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
          />
        </div>

        <div>
          <label htmlFor="message" className="block text-sm font-medium">
            Message:
          </label>
          <textarea
            id="message"
            name="message"
            value={formData.message}
            onChange={handleInputChange}
            rows={4}
            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
          />
        </div>

        <button 
          type="submit" 
          disabled={status.type === 'loading'}
          className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
        >
          {status.type === 'loading' ? 'Submitting...' : 'Submit React Form'}
        </button>

        {status.message && (
          <div className={`p-3 rounded-md ${
            status.type === 'success' 
              ? 'bg-green-50 text-green-800' 
              : 'bg-red-50 text-red-800'
          }`}>
            {status.message}
          </div>
        )}
      </form>
    </div>
  );
}

export default KirokuFormReactExample;

Server-Side Submission (Node.js Example)

For maximum security (protecting your API Key) and for backend workflows, submit data to KirokuForms from your server. Here's a conceptual example using Node.js and Express.

JavaScript (Node.js/Express)
// Example: Server-Side Submission with Node.js and Express
// 1. Frontend HTML (posts to your backend)
// <form action="/your-backend-submit-route" method="POST">
//   <input type="email" name="email" />
//   <button type="submit">Submit via Backend</button>
// </form>

// 2. Backend Express Route (e.g., server.js)
const express = require('express');
const fetch = require('node-fetch'); // or another HTTP client
const app = express();
app.use(express.urlencoded({ extended: true })); // To parse form data

const KIROKU_FORM_ID = 'YOUR_FORM_ID';
const KIROKU_API_KEY = 'YOUR_SECRET_API_KEY'; // Keep this secret on the server!

app.post('/your-backend-submit-route', async (req, res) => {
  const submissionData = req.body; // Contains { email: '...' }

  try {
    const kirokuResponse = await fetch(`https://www.kirokuforms.com/api/forms/${KIROKU_FORM_ID}/submit`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${KIROKU_API_KEY}`,
        'Accept': 'application/json'
      },
      body: JSON.stringify(submissionData)
    });

    const result = await kirokuResponse.json();

    if (kirokuResponse.ok) {
      // Handle success (e.g., redirect user, show success message)
      res.status(200).send(`Successfully submitted to KirokuForms: ${JSON.stringify(result)}`);
    } else {
      // Handle error from KirokuForms
      console.error('KirokuForms API Error:', result);
      res.status(kirokuResponse.status).send(`Error submitting to KirokuForms: ${result.error?.message || 'Unknown error'}`);
    }
  } catch (error) {
    console.error('Error in backend submission:', error);
    res.status(500).send('Internal server error.');
  }
});

// app.listen(3000, () => console.log('Your backend server running on port 3000'));

Working with File Uploads

KirokuForms supports file uploads. When your form includes a field of type 'file', your JavaScript submission logic needs to use FormData to correctly send the file(s). Our Fetch API and React examples (generated by generateFetchExampleSet and generateReactExampleCode respectively) automatically detect file input fields and use FormData. Refer to those examples when implementing file uploads. Important: When using FormData for file uploads, do not manually set the Content-Type header in your fetch request. The browser will set it correctly to multipart/form-data along with the necessary boundary.


Ready to Build?

Dive deeper into our documentation or start integrating KirokuForms today.