https://api.formpaste.com/submit con el access_key de tu formulario, y
maneja la respuesta de éxito/error. Son intencionalmente simples: un campo
de correo, un campo de mensaje y manejo del envío, no una librería de
componentes con estilo. Cambia YOUR_ACCESS_KEY por la clave de acceso que
se muestra en la pestaña Setup de tu formulario.
- HTML
- React
- Next.js
- Astro
- Vue
- Svelte
Un formulario simple, sin necesidad de JavaScript.
<form action="https://api.formpaste.com/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input type="email" name="email" placeholder="Your email" required>
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit">Send</button>
</form>
- TypeScript
- JavaScript
import { useState } from "react";
export function ContactForm() {
const [status, setStatus] = useState<"idle" | "sending" | "ok" | "error">("idle");
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus("sending");
const form = new FormData(e.currentTarget);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form as any) });
setStatus(res.ok ? "ok" : "error");
}
if (status === "ok") return <p>Thanks - your message was sent.</p>;
return (
<form onSubmit={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={status === "sending"}>Send</button>
{status === "error" && <p>Something went wrong. Please try again.</p>}
</form>
);
}
import { useState } from "react";
export function ContactForm() {
const [status, setStatus] = useState("idle"); // "idle" | "sending" | "ok" | "error"
async function onSubmit(e) {
e.preventDefault();
setStatus("sending");
const form = new FormData(e.currentTarget);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form) });
setStatus(res.ok ? "ok" : "error");
}
if (status === "ok") return <p>Thanks - your message was sent.</p>;
return (
<form onSubmit={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={status === "sending"}>Send</button>
{status === "error" && <p>Something went wrong. Please try again.</p>}
</form>
);
}
Un componente de cliente, agrega
"use client" ya que mantiene el estado del formulario.- TypeScript
- JavaScript
"use client";
import { useState } from "react";
export function ContactForm() {
const [status, setStatus] = useState<"idle" | "sending" | "ok" | "error">("idle");
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus("sending");
const form = new FormData(e.currentTarget);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form as any) });
setStatus(res.ok ? "ok" : "error");
}
if (status === "ok") return <p>Thanks - your message was sent.</p>;
return (
<form onSubmit={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={status === "sending"}>Send</button>
{status === "error" && <p>Something went wrong. Please try again.</p>}
</form>
);
}
"use client";
import { useState } from "react";
export function ContactForm() {
const [status, setStatus] = useState("idle"); // "idle" | "sending" | "ok" | "error"
async function onSubmit(e) {
e.preventDefault();
setStatus("sending");
const form = new FormData(e.currentTarget);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form) });
setStatus(res.ok ? "ok" : "error");
}
if (status === "ok") return <p>Thanks - your message was sent.</p>;
return (
<form onSubmit={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={status === "sending"}>Send</button>
{status === "error" && <p>Something went wrong. Please try again.</p>}
</form>
);
}
Un formulario sin JS, funciona igual que la versión HTML simple.
---
// src/components/ContactForm.astro - a no-JS form that posts to Formpaste.
---
<form action="https://api.formpaste.com/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit">Send</button>
</form>
- TypeScript
- JavaScript
<script setup lang="ts">
import { ref } from "vue";
const status = ref<"idle" | "sending" | "ok" | "error">("idle");
async function onSubmit(e: Event) {
status.value = "sending";
const form = new FormData(e.target as HTMLFormElement);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form as any) });
status.value = res.ok ? "ok" : "error";
}
</script>
<template>
<p v-if="status === 'ok'">Thanks - your message was sent.</p>
<form v-else @submit.prevent="onSubmit">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit" :disabled="status === 'sending'">Send</button>
<p v-if="status === 'error'">Something went wrong. Please try again.</p>
</form>
</template>
<script setup>
import { ref } from "vue";
const status = ref("idle"); // "idle" | "sending" | "ok" | "error"
async function onSubmit(e) {
status.value = "sending";
const form = new FormData(e.target);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form) });
status.value = res.ok ? "ok" : "error";
}
</script>
<template>
<p v-if="status === 'ok'">Thanks - your message was sent.</p>
<form v-else @submit.prevent="onSubmit">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit" :disabled="status === 'sending'">Send</button>
<p v-if="status === 'error'">Something went wrong. Please try again.</p>
</form>
</template>
- TypeScript
- JavaScript
<script lang="ts">
let status: "idle" | "sending" | "ok" | "error" = "idle";
async function onSubmit(e: SubmitEvent) {
status = "sending";
const form = new FormData(e.target as HTMLFormElement);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form as any) });
status = res.ok ? "ok" : "error";
}
</script>
{#if status === "ok"}
<p>Thanks - your message was sent.</p>
{:else}
<form on:submit|preventDefault={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit" disabled={status === "sending"}>Send</button>
{#if status === "error"}<p>Something went wrong. Please try again.</p>{/if}
</form>
{/if}
<script>
let status = "idle"; // "idle" | "sending" | "ok" | "error"
async function onSubmit(e) {
status = "sending";
const form = new FormData(e.target);
const res = await fetch("https://api.formpaste.com/submit", { method: "POST", body: new URLSearchParams(form) });
status = res.ok ? "ok" : "error";
}
</script>
{#if status === "ok"}
<p>Thanks - your message was sent.</p>
{:else}
<form on:submit|preventDefault={onSubmit}>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="email" name="email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit" disabled={status === "sending"}>Send</button>
{#if status === "error"}<p>Something went wrong. Please try again.</p>{/if}
</form>
{/if}
Descubre nuestras plantillas de formulario de contacto personalizables y listas para copiar y pegar.
Obtener el snippet con tu clave de acceso real ya incluida
En lugar de copiar y pegarYOUR_ACCESS_KEY a mano, puedes:
- Copiar el snippet desde la pestaña Setup del formulario en el panel, ya tiene tu clave de acceso incorporada.
- Pedirle a un agente conectado a través del servidor MCP que llame a
get_snippetcon tuformId, devuelve el mismo snippet con la clave de acceso real ya incluida en lugar de un marcador de posición.
Nombres de campo reservados
/submit maneja algunos nombres de campo de forma especial, evita reutilizarlos
para tus propios datos:
| Campo | Propósito |
|---|---|
access_key | Obligatorio. Identifica a qué formulario pertenece el envío. |
botcheck | Honeypot. Déjalo vacío y oculto para los visitantes reales, consulta Filtrado de spam. |
_ts | Opcional. Una marca de tiempo usada para heurísticas de spam basadas en el tiempo. |
redirect | Opcional. Una URL a la que redirigir tras un envío exitoso sin JS, consulta Redirecciones. |
email, message, name, o
cualquier otro que necesite tu formulario.