Si buscó Contacto de exportación estática de next.js formulario sin backend, you are probably at the point where the UI is done and the deployment target is the problem. The form works locally through an API route, or a tutorial told you to use a server action, but your production build is static files. There is no Node process waiting for /api/contact.
La solución es dejar de tratar el formulario de contacto como un problema interno de API. Un navegador puede publicar un formulario HTML normal en un endpoint externo. Su aplicación Next.js permanece estática, su host solo sirve archivos y el backend de formularios maneja la entrega, las comprobaciones antispam, las redirecciones, los webhooks y las integraciones. FormsFort es una opción para ese endpoint; la misma arquitectura se cubre de manera más general en Formulario HTML sin backend guide.
Por qué la exportación estática interrumpe las rutas API y las acciones del servidor
Next.js puede representar páginas de varias maneras. Con una implementación de servidor, las rutas API, los controladores de ruta, el middleware y las acciones del servidor se pueden ejecutar cuando llega una solicitud. Con la exportación estática, Next.js escribe archivos en el disco. El resultado es HTML, CSS, JavaScript, imágenes y otros activos que un host estático puede servir directamente.
That difference matters for forms. After export, a file host can serve /contact/index.html, but it cannot execute app/api/contact/route.ts. A POST to /api/contact often becomes a 404 because no file exists at that path, or a 405 because the host can only serve a static asset and does not accept POST there.
Las acciones del servidor tienen el mismo problema. Son características del servidor. Se pueden compilar en una implementación de servidor, pero no le dan a GitHub Pages ni a un depósito de almacenamiento de objetos un controlador de solicitudes. Para la exportación estática, diseñe el formulario en torno a lo que el navegador puede hacer por sí solo: enviar campos a una URL absoluta.
La acción del formulario es el límite del backend
A plain HTML form already knows how to make an HTTP request. The action attribute is the destination. The method controls the verb. Inputs with a name become the submitted payload. When the user clicks submit, the browser sends the form body to the action URL.
En un sitio Next.js estático, la acción debe apuntar fuera de su aplicación:
<form action="https://api.formsfort.com/submit" method="POST">
...
</form> . Esa URL recibe el envío y realiza el trabajo que su sitio exportado no puede realizar: validación, filtrado de abuso, entrega de correo electrónico, envío de webhook e integraciones opcionales. Si necesita una descripción general más amplia del endpoint antes de conectar Next.js, lea endpoint de formulario guide.
Configuración de exportación estática
Su configuración de Next.js puede permanecer enfocada en la build estático. No necesita una ruta API para el formulario de contacto y no necesita reescribirla para enviar la solicitud.
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
trailingSlash: true,
};
export default nextConfig; trailingSlash is optional, but it is common for GitHub Pages and static hosts that prefer directory-style routes. The important line is output: "export". Once you choose that deployment mode, keep form processing outside the app.
Un componente de formulario de contacto de React simple
This component works in the App Router or Pages Router because it renders ordinary markup. It can live at app/contact/page.tsx, inside a shared component, or in a static page.
export default function ContactForm() {
return (
<form action="https://api.formsfort.com/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input
type="hidden"
name="redirect"
value="https://example.com/contact/thanks/"
/>
<div>
<label htmlFor="name">Name</label>
<input id="name" name="name" type="text" autoComplete="name" required />
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
/>
</div>
<div>
<label htmlFor="message">Message</label>
<textarea id="message" name="message" rows={6} required />
</div>
<input
type="checkbox"
name="botcheck"
tabIndex={-1}
autoComplete="off"
style={{ display: "none" }}
/>
<button type="submit">Send message</button>
</form>
);
}
Every submitted field needs a name. The labels and browser validation are for the user. The names are what the endpoint receives.
Campos para incluir
| Campo | Propósito | Nota de exportación estática |
|---|---|---|
access_key | Dirige el envío al formulario correcto. | Esta es una configuración de formulario público, no un secreto de servidor privado. |
name, email, message | La payload del formulario de contacto real. | Use normal inputs with stable name attributes. |
botcheck | Campo Honeypot para filtrado básico de spam. | Ocultarlos de las personas; El spam automatizado a menudo lo llena de todos modos. |
redirect | A dónde va el navegador después de un envío exitoso sin JS. | Utilice una URL absoluta o una ruta que sirva su host estático. |
captcha_token | Opcional protección contra abuso más fuerte. | Generarlo en el navegador, luego enviarlo con el formulario. |
Do not put a private API key in a hidden input or in NEXT_PUBLIC_ variables. A hidden input is hidden in the page, not hidden from the internet. The access key is meant to be public. Server-only credentials are not.
Manejar JavaScript y rutas sin JavaScript
El formulario estático más robusto comienza como HTML simple. Si JavaScript falla, el usuario aún puede enviar y acceder a la página de redireccionamiento configurada. Eso es importante en sitios estáticos porque no hay ningún servidor alternativo que usted controle.
You can then enhance the form with JavaScript for inline feedback. In Next.js, that usually means a client component that intercepts submit, sends FormData with fetch, and updates local state. Keep the same action and method on the form so the no-JS path remains valid.
"use client";
import { useState } from "react";
export default function AjaxContactForm() {
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
setStatus("sending");
const form = event.currentTarget;
const formData = new FormData(form);
const response = await fetch(form.action, {
method: "POST",
body: formData,
headers: {
Accept: "application/json",
},
});
if (response.ok) {
form.reset();
setStatus("sent");
return;
}
setStatus("error");
}
return (
<form action="https://api.formsfort.com/submit" method="POST" onSubmit={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<input type="checkbox" name="botcheck" style={{ display: "none" }} />
<button type="submit" disabled={status === "sending"}>
{status === "sending" ? "Sending..." : "Send"}
</button>
{status === "sent" && <p>Message sent.</p>}
{status === "error" && <p>Something went wrong. Please try again.</p>}
</form>
);
} La versión AJAX es opcional. Mejora la experiencia, pero también introduce CORS y el manejo de errores del lado del cliente. Para muchas páginas de contacto de producción, el POST simple con redireccionamiento es más simple y confiable.
Usar redirecciones en hosts estáticos
With a no-JS form, the browser navigates away after submit. Set a redirect field to a static thank-you page you control, such as https://example.com/contact/thanks/. Create that route as a normal static Next.js page.
<input
type="hidden"
name="redirect"
value="https://example.com/contact/thanks/"
/> Utilice la URL de producción final cuando pruebe las redirecciones. Las rutas relativas pueden comportarse de manera diferente según el endpoint y la referencia, mientras que las URL absolutas hacen explícito el destino esperado.
protección antispam sin servidor
Una exportación estática no puede ejecutar su propio filtro antispam de servidor, pero sí puede recopilar señales para un backend de formularioss. Empiece con un honeypot. Es barato, funciona sin JavaScript y bloquea envíos automatizados de bajo esfuerzo.
<input
type="checkbox"
name="botcheck"
tabindex="-1"
autocomplete="off"
style="display:none"
/> Si su formulario está expuesto a un abuso mayor, agregue captcha. Con FormsFort, el captcha personalizado pertenece a Pro, junto con los webhooks, la carga de archivos, Google Sheets y el escaneo. Compare el plan actual en precios.
Dónde encajan los webhooks
Los webhooks son la trampilla de escape de producción para formularios estáticos. En lugar de pedirle a su sitio estático que procese el envío, el backend de formularios lo recibe y luego llama a un servidor que usted controla, una plataforma de automatización u otra API. Esto mantiene el sitio público simple y al mismo tiempo permite que el flujo de trabajo de su negocio se ejecute en el lado del servidor.
Si el formulario de contacto debe crear tickets, enriquecer clientes potenciales o notificar a otro sistema, lea Backend de formulario con webhooks. Para obtener una configuración más general de Next.js que no se limite al modo de exportación, consulte Formulario de contacto de Next.js.
Solución de problemas de formularios de contacto de exportación estáticos
POST to /api/contact returns 404 or 405
You are still posting to your own app. In static export, that route is not a running handler. Change the form action to the external endpoint, or deploy Next.js to a server/runtime that supports API routes.
El formulario recarga la página pero no llega nada
Check that the form has method="POST". Without it, browsers default to GET, which puts fields into the URL and does not create the expected submission. Also confirm the access key field is present and named exactly access_key.
AJAX falla con un error CORS
A normal HTML form submit does not use CORS the same way fetch does. If the plain form works but AJAX fails, inspect the endpoint response and allowed origin. Use Accept: application/json, send FormData directly, and test from the deployed domain, not only localhost.
La redirección exitosa no va a donde se esperaba
Utilice una URL de redireccionamiento absoluto y asegúrese de que la página de agradecimiento exista en el resultado exportado. Los hosts estáticos normalmente normalizan las barras diagonales, así que pruebe la URL de producción exacta después de la implementación.
Las pruebas de spam dan falsa confianza
No pruebes únicamente rellenando el formulario manualmente. Envíelo una vez con el honeypot marcado para confirmar que el backend lo rechaza. Envíe rápidamente varias veces para comprender la limitación de tasas. Si habilita captcha, pruebe tanto un token válido como un token faltante.
Faltan variables de entorno después de la exportación
In Next.js, client-side environment variables must be prefixed with NEXT_PUBLIC_ and are baked into the bundle at build time. That is fine for a public form access key, but not for secrets. If you changed an environment variable after building, rebuild and redeploy the static output.
Lista de verificación de producción
- Use an absolute external
actionURL. - Set
method="POST". - Give every submitted field a
name. - Utilice una clave de acceso pública, no un secreto de servidor privado.
- Agregue un campo de honeypot y pruebe que sea rechazado.
- Cree una página de agradecimiento estática y utilice una URL de redireccionamiento absoluto.
- Pruebe el dominio desplegado, no solo el desarrollo local.
- Utilice AJAX solo cuando el estado en línea valga la pena por el CORS adicional y el manejo del estado.
El modelo de trabajo es simple: Next.js exporta la página, el navegador envía el formulario y el endpoint externo procesa el mensaje. Ese es el límite correcto para el alojamiento estático. Para obtener más variantes de copiar y pegar, explore ejemplos page.