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.
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
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.
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;