npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@k34a/forms

v0.0.40

Published

Create and share forms, and allow users to fill them.

Readme

@k34a/forms

Dynamic Form Builder & Handler for Next.js and Supabase
Easily create, render, and manage dynamic forms directly from your admin panel.
Supports form validation, user-friendly UI (powered by Mantine), and built-in submission handling.

Features

  • Dynamic form rendering using JSON schema
  • Automatic form validation with Zod
  • Form schema management from your Supabase database
  • Built-in notification hooks for success, error, and validation feedback
  • Easy API integration for form submission
  • Custom callback support (e.g., Telegram notifications)

Installation

npm install @k34a/forms

Required peer dependencies

Make sure you already have these installed in your project:

npm install @mantine/core @mantine/dates @mantine/dropzone @mantine/notifications @supabase/supabase-js @tabler/icons-react react zod

Required versions:

| Package | Version | |----------|----------| | @mantine/core | ≥ 8.0.0 | | @mantine/dates | ≥ 8.0.0 | | @mantine/dropzone | ≥ 8.0.0 | | @mantine/notifications | ≥ 8.0.0 | | @supabase/supabase-js | ≥ 2.52.0 | | @tabler/icons-react | ≥ 3.0.0 | | react | ≥ 19.1.0 | | zod | ≥ 4.0.0 |

⚙️ API Setup

You might need to create an API route in your application to handle form submissions.

Example using Next.js Route Handlers (app/api/fill-me/[formType]/route.ts):

import { adminPanelLink, ORG_ID } from "@/config/config";
import { supabaseAdmin } from "@/lib/db/supabase";
import { sendTelegramMessage } from "@/lib/telegram";
import { FormFillingService } from "@k34a/forms";
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

function isPlainObject(input: unknown): input is Record<string, any> {
  return typeof input === "object" && input !== null && !Array.isArray(input);
}

async function getSourceDetails() {
  const hdrs = await headers();
  const userAgent = hdrs.get("user-agent") ?? null;
  const xff = hdrs.get("x-forwarded-for");
  const realIp = hdrs.get("x-real-ip");
  const sourceIp = xff?.split(",")[0].trim() ?? realIp ?? null;
  return { sourceIp, userAgent };
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ formType: string }> },
) {
  const { formType } = await params;

  let body: unknown;
  try {
    body = await request.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }

  if (!isPlainObject(body)) {
    return NextResponse.json(
      { error: "Invalid request format. Expected a key-value object." },
      { status: 400 }
    );
  }

  try {
    const service = new FormFillingService(
      adminPanelLink,
      ORG_ID,
      supabaseAdmin,
      async (msg) => {
        try {
          await sendTelegramMessage(msg);
        } catch (error) {
          console.log(error);
        }
      },
    );

    const userDetails = await getSourceDetails();
    const result = await service.fillForm(
      formType,
      body,
      userDetails.sourceIp ?? "",
      userDetails.userAgent ?? "",
    );

    return NextResponse.json(result);
  } catch (err: any) {
    return NextResponse.json({ error: err.message || "Internal Server Error" }, { status: 500 });
  }
}

Explanation of Methods & Callbacks

| Function / Callback | Description | |---------------------|-------------| | isPlainObject() | Ensures the incoming JSON body is a plain object (key-value pairs) and not an array or invalid input. | | getSourceDetails() | Extracts the client’s IP address and User-Agent from headers for logging or analytics. | | FormFillingService | Core service from @k34a/forms that handles form validation, storage, and notifications. | | fillForm(formType, body, ip, userAgent) | Saves and validates the submitted form data. It links the form type to the schema defined in your admin panel. | | Telegram Callback (async (msg) => { ... }) | Optional async callback triggered after a successful form submission. You can send notifications to Telegram, Slack, etc. |

Creating a Dynamic Form Component

The simplest way to render a form dynamically is by using the FormBuilder component.

"use client";

import { FormBuilder, FormSchema } from "@k34a/forms";
import z from "zod";
import { notifications } from "@mantine/notifications";
import { ORG_ID } from "@/config/config";

interface FormProps {
  schema: z.infer<typeof FormSchema>;
  formType: string;
}

export const FillMe = (props: FormProps) => {
  return (
    <FormBuilder
      mode="fill"
      schema={props.schema}
      formType={props.formType}
      orgId={ORG_ID}
      submissionAPIEndPoint={`/api/fill-me/${props.formType}`}
      onSuccess={() =>
        notifications.show({
          title: "All Set!",
          message:
            "Your details were submitted successfully. Thank you for completing the form!",
          color: "green",
        })
      }
      onValidationError={() =>
        notifications.show({
          title: "Please Review Your Form",
          message:
            "Some information seems to be missing or incorrect. Check the highlighted fields and try again.",
          color: "orange",
        })
      }
      onError={() =>
        notifications.show({
          title: "Submission Failed",
          message:
            "Something went wrong while sending your details. Please try again later.",
          color: "red",
        })
      }
    />
  );
};

💡 Tip: Make sure to set your organization ID (ORG_ID) from your admin panel at k34a.vercel.app.

Fetching the Form Schema from Supabase

You can dynamically fetch a form schema from your Supabase database before rendering the form:

import { FormFillingService } from "@k34a/forms";
import { supabaseAdmin } from "@/lib/db/supabase";
import { notFound } from "next/navigation";
import { adminPanelLink, ORG_ID } from "@/config/config";
import Partners from "@/components/partners/partners";

export default async function PartnersPage() {
  let schema;
  const formType = "csr_partnership_inquiry";

  try {
    schema = await new FormFillingService(
      adminPanelLink,
      ORG_ID,
      supabaseAdmin,
    ).getFormSchema(formType);
  } catch (err) {
    console.error(err);
    notFound();
  }

  return (
    <main>
      <Partners schema={schema} formType={formType} />
    </main>
  );
}

Summary

| Step | Description | |------|--------------| | Install the package | npm install @k34a/forms | | Create API handler | Accepts and validates form submissions | | Use FormBuilder | To render and handle forms in your UI | | Fetch form schemas | From your Supabase database using FormFillingService | | Customize callbacks | (Success, ValidationError, Error) for better UX |

License

MIT © 2025 — Built with ❤️ by K34A