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

# Write a landing page by hand

> What a landing page's HTML needs: the document, the allGood script, a form, images and component markers.

A landing page is one HTML file that allGood serves as written. You can change it in **Edit Source**, or ask Mary to make a change. This article covers what the HTML needs so the page publishes, tracks and captures correctly. For the marketer view, see [Editing a landing page's code](https://docs.allgoodhq.com/mk/content/landing-pages/editing-the-code.md).

## The document

Write a complete, standalone HTML5 page:

* Start with `<!DOCTYPE html>` and `<html lang="...">`.
* In `<head>`, include a `<title>`, `<meta charset>`, the viewport tag and a `<meta name="description">`. The platform doesn't add or edit these. They ship as you write them.
* Inline your CSS in one `<style>` block, or in style attributes. The page is served as a single file, so a relative link to a style file will fail once it's published, even if it looked fine in the preview.
* A web font through a `<link>` to a public font service is fine. Large framework bundles aren't.
* Give every `<img>` a meaningful `alt`.

The page's address is its **slug**, such as `spring-launch`. It's set with the page, not in the HTML, and **Edit Source** doesn't change it. Your edits keep the slug the page already has. To change it, ask Mary for a new version.

## The allGood script

Add one script tag in `<head>`, on every page:

```html theme={null}
<script src="/_ag/v1.js" async></script>
```

Don't add a key. The page is served from your own host, which tells allGood whose page it is. The script records the page view and waits for the visitor's consent first, so don't gate it yourself. A page without the tag is never tracked.

## Add a form

The page owns the markup. The form is a separate asset that owns what's collected. Point the form tag at it with a citation, never a literal ID:

```html theme={null}
<form data-ag-form="{{ asset.signup.formId }}">
  <label>Email <input name="email" type="email" required></label>
  <label>
    <input type="checkbox" name="consent">
    <span data-ag-consent></span>
  </label>
  <button type="submit">Get the guide</button>
</form>
<p data-ag-form-status="{{ asset.signup.formId }}" role="status" aria-live="polite"></p>
```

* **Declare the form.** A citation like `{{ asset.signup.formId }}` needs a matching entry in the page's list of assets it uses. A citation with no match is rejected on save. If you add a form by hand, ask Mary to declare it.
* **Let the script do the work.** It submits the form, adds the honeypot and the bot check when the form needs them, locks against double submits and shows the result. Don't add your own submit handler, honeypot field or bot check.
* **Don't write the consent words.** Leave the empty `data-ag-consent` span. The script fills in the registered wording, so the words a visitor sees are the words the submission records.
* **Put the status element after `</form>`**, so it isn't hidden with the form when a submission is accepted.
* **Match field names** to the form's field names, and always include `email`.

To react to a submission, listen for `allgood:form:submitted` and `allgood:form:rejected` on `document`. Filter on `event.detail.formId`, and keep the handler synchronous because the event fires before any redirect. See [Capture a form](https://docs.allgoodhq.com/mk/developer/web-edge/capture-a-form.md).

## Add an image from the library

Cite a library image instead of hard-coding its address:

```html theme={null}
<img src="{{ asset.hero.url }}" alt="Product dashboard">
```

As with forms, the citation needs a matching entry in the page's list of assets. A plain image address or a data URI needs no entry.

## Use component blocks

If the page was built from a [page component library](https://docs.allgoodhq.com/mk/content/landing-pages/page-components.md), each section carries a marker such as `data-ag-component="blocks:hero-split"`. When you save, allGood checks that each marked section still has its block's elements and nesting. Your words and styling values can change. Adding, removing or re-nesting elements inside a marked section can't.

## Things to avoid

* **A person's details.** The page is built once at publish time, so `{{ person.* }}` is refused.
* **Pasted tags and consent.** allGood's consent settings cover allGood's own tracking. Tags you paste in, such as analytics or ad pixels, run as written.
* **Your own submit code.** It can double-count leads.

## Related articles

* [Editing a landing page's code](https://docs.allgoodhq.com/mk/content/landing-pages/editing-the-code.md)
* [Capture a form](https://docs.allgoodhq.com/mk/developer/web-edge/capture-a-form.md)
* [Install the tag](https://docs.allgoodhq.com/mk/developer/web-edge/install-the-tag.md)


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