> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surveyowl.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Nuxt

> Connect a Nuxt 3 page to an Endpoint form.

This guide is written for Nuxt 3 with `<script setup>`. You will keep the Public Key in runtime config and submit from a normal page.

## 1. Save your Public Key

Copy the Public Key from the Endpoint form's **General** tab. Then register it in `nuxt.config.ts`:

```ts nuxt.config.ts theme={null}
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      surveyowlKey: '', // filled from NUXT_PUBLIC_SURVEYOWL_KEY
    },
  },
});
```

Put the real value in a `.env` file:

```bash .env theme={null}
NUXT_PUBLIC_SURVEYOWL_KEY=YOUR_FORM_PUBLIC_KEY
```

The key is public by design: it can only add responses to this form. Restart `nuxt dev` after creating the file.

## 2. Create the page

```vue pages/contact.vue theme={null}
<script setup lang="ts">
const { public: config } = useRuntimeConfig();
const endpoint = `https://api.surveyowl.co/v1/submit/${config.surveyowlKey}`;

const status = ref('');
const sending = ref(false);

async function onSubmit(event: Event) {
  const form = event.target as HTMLFormElement;
  sending.value = true;
  status.value = '';

  try {
    const response = await fetch(endpoint, {
      method: 'POST',
      body: new FormData(form),
    });
    const result = await response.json();

    if (!response.ok) {
      const message =
        typeof result.error === 'string' ? result.error : result.error?.message;
      throw new Error(message || 'Could not send the form.');
    }

    form.reset();
    status.value = 'Thanks — your message was sent.';
  } catch (error) {
    status.value = error instanceof Error ? error.message : 'Something went wrong.';
  } finally {
    sending.value = false;
  }
}
</script>

<template>
  <main>
    <h1>Contact us</h1>

    <form @submit.prevent="onSubmit">
      <label for="name">Name</label>
      <input id="name" name="name" required />

      <label for="email">Email</label>
      <input id="email" name="email" type="email" required />

      <label for="message">Message</label>
      <textarea id="message" name="message" required />

      <button type="submit" :disabled="sending">
        {{ sending ? 'Sending…' : 'Send message' }}
      </button>
      <p role="status">{{ status }}</p>
    </form>
  </main>
</template>
```

Visit `/contact`, send a test response, and open **Submissions** to confirm it arrived.

## Good to know

* The request uses the browser's `fetch`, not `$fetch`. `fetch` lets you read the error message from a `400`, `402`, or `403` response without extra handling.
* The submit happens in the browser, so server rendering and static generation (`nuxt generate`) both work.
* Using [domain restriction](/developer-api/submit-a-response#domain-restriction)? Add your production domain and `localhost` before testing.
* For file uploads or CAPTCHA, keep the `FormData` body and follow [Files and CAPTCHA](/developer-api/files-and-captcha).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.