Basestack Docs

NextJS & React

React is a JavaScript library for building user interfaces, allowing developers to create interactive, dynamic web applications with reusable components. Next.js is a React framework that enables server-side rendering, static site generation, and simplified routing for building fast, scalable web applications.

Prerequisites

Before you begin, make sure you have:

  • A Basestack Forms account
  • A React or Next.js project with a form component

This guide works with both React and Next.js applications. The implementation is the same for both frameworks.

Quick Setup Guide

Follow these steps to integrate Basestack Forms with your React or Next.js application:

Choose your HTTP client

You can use any HTTP client to send form data from your React component. Common options include:

  • Fetch API - Built into browsers, no additional dependencies
  • Axios - Popular library with interceptors and better error handling
  • Any other HTTP client that supports POST requests

Include mode=rest in your endpoint URL query string to receive JSON responses. This allows you to handle success and error states in your component.

For more advanced form handling, consider using libraries like React Hook Form or Formik. These can simplify form state management and validation.

Implement form submission handler

Copy the example component below into your React project. The example demonstrates:

  • Form state management using React hooks
  • Form submission handling with Fetch API
  • Basic error handling and success feedback

Customize the form fields and submission logic to match your requirements.

contact.tsx
import React, { useState } from "react";

const FormComponent = () => {
  // You can also use rect-hook-form or formik for form handling
  // check out the docs for more info https://react-hook-form.com/
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");

  const onHandleSubmit = (e) => {
    e.preventDefault();
    e.stopPropagation();

    // You can also use axios or any other library to make the request
    // You can also use async await to make the request
    fetch("https://forms-api.basestack.co/v1/s/[KEY]?mode=rest", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name, email, message }),
    })
      .then((res) => res.json())
      .then((data) => {
        if (data.code === 200) {
          console.log("Form submitted successfully");
        }
      })
      .catch((error) => {
        console.log("Error submitting form", error);
      });
  };

  return (
    <form onSubmit={(e) => onHandleSubmit(e)}>
      <div>
        <label htmlFor="name">Name:</label>
        {/*  <!-- name each of your inputs as you wish --> */}
        <input
          type="text"
          id="name"
          name="name"
          required
          autoComplete="name"
          aria-label="Name"
          placeholder="Enter your name"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </div>

      <div>
        <label htmlFor="email">Email:</label>
        <input
          type="email"
          id="email"
          name="email"
          required
          autoComplete="email"
          aria-label="Email"
          placeholder="Enter your email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
      </div>

      <div>
        <label htmlFor="message">Message:</label>
        <textarea
          id="message"
          name="message"
          required
          aria-label="Message"
          placeholder="Enter your message"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
        ></textarea>
      </div>

      {/*  <!-- your other form fields go here --> */}

      <button type="submit">Submit</button>
    </form>
  );
};

export default FormComponent;

Attaching files

File uploads must be enabled in Settings โ†’ General first (off by default on new forms). Switch the body from JSON.stringify(...) to a FormData instance, and add an <input type="file"> whose value you read from a ref. The browser will set the multipart Content-Type (with boundary) automatically โ†’ do not set it yourself.

Each file must be 1 MB or smaller, and a single submission may include at most 5 files. The server returns HTTP 413 if either limit is exceeded. See File Uploads โ†’ for full details.

Simple example: single file

contact-with-file.tsx
import React, { useRef, useState } from "react";

const ContactWithFile = () => {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const onSubmit = async (event: React.FormEvent) => {
    event.preventDefault();

    const formData = new FormData();
    formData.append("name", name);
    formData.append("email", email);
    formData.append("message", message);

    const resume = fileInputRef.current?.files?.[0];
    if (resume) {
      formData.append("resume", resume);
    }

    const res = await fetch(
      "https://forms-api.basestack.co/v1/s/[KEY]?mode=rest",
      {
        method: "POST",
        headers: { Accept: "application/json" },
        // Do NOT set Content-Type โ€” the browser handles the boundary.
        body: formData,
      },
    );

    const data = await res.json();
    if (data.code === 200) {
      console.log("Submission stored");
    } else {
      console.warn(data.message);
    }
  };

  return (
    <form onSubmit={onSubmit}>
      <input
        type="text"
        name="name"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
        required
      />
      <input
        type="email"
        name="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <textarea
        name="message"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Message"
        required
      />
      <input
        type="file"
        name="resume"
        ref={fileInputRef}
        accept="application/pdf"
      />
      <button type="submit">Send</button>
    </form>
  );
};

export default ContactWithFile;

Complex example: multiple files with picker state and validation

When you support several files, keep them in component state so you can show a preview list, validate before submitting, and let the user remove individual attachments.

application-with-files.tsx
import React, { useRef, useState } from "react";

const MAX_FILE_SIZE = 1 * 1024 * 1024; // 1 MB
const MAX_FILES = 5;

const ApplicationForm = () => {
  const [files, setFiles] = useState<File[]>([]);
  const inputRef = useRef<HTMLInputElement | null>(null);

  const onPickFiles = (event: React.ChangeEvent<HTMLInputElement>) => {
    const picked = Array.from(event.target.files ?? []);
    event.target.value = "";

    setFiles((prev) => {
      const remaining = Math.max(0, MAX_FILES - prev.length);
      const accepted: File[] = [];

      for (const file of picked) {
        if (file.size > MAX_FILE_SIZE) {
          alert(`"${file.name}" exceeds the 1 MB limit.`);
          continue;
        }
        if (accepted.length >= remaining) {
          alert(`Up to ${MAX_FILES} files allowed.`);
          break;
        }
        accepted.push(file);
      }

      return [...prev, ...accepted];
    });
  };

  const removeFile = (index: number) =>
    setFiles((prev) => prev.filter((_, i) => i !== index));

  const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    const form = event.currentTarget;
    const formData = new FormData();
    formData.append("full_name", (form.full_name as HTMLInputElement).value);
    formData.append("email", (form.email as HTMLInputElement).value);
    for (const file of files) {
      formData.append("portfolio", file);
    }

    const res = await fetch(
      "https://forms-api.basestack.co/v1/s/[KEY]?mode=rest",
      {
        method: "POST",
        headers: { Accept: "application/json" },
        body: formData,
      },
    );

    const data = await res.json();
    if (data.code === 200) {
      form.reset();
      setFiles([]);
    } else {
      alert(data.message ?? "Submission failed.");
    }
  };

  return (
    <form onSubmit={onSubmit}>
      <input name="full_name" placeholder="Full name" required />
      <input name="email" type="email" placeholder="Email" required />

      <button
        type="button"
        onClick={() => inputRef.current?.click()}
        disabled={files.length >= MAX_FILES}
      >
        Attach files ({files.length}/{MAX_FILES})
      </button>
      <input
        ref={inputRef}
        type="file"
        multiple
        hidden
        onChange={onPickFiles}
      />

      <ul>
        {files.map((file, index) => (
          <li key={`${file.name}-${index}`}>
            {file.name} ยท {(file.size / 1024).toFixed(1)} KB{" "}
            <button type="button" onClick={() => removeFile(index)}>
              Remove
            </button>
          </li>
        ))}
      </ul>

      <button type="submit">Submit application</button>
    </form>
  );
};

export default ApplicationForm;

On this page