Building a Hyperapp
What you make, what you make it with, and the parts some Hyperapps add.
A Hyperapp is a website, so building one is building a site. What makes it a Hyperapp is conformance to the specification: a Directory Page at the root, capability pages holding Actions, a Response Page for every submission, and pages that survive conversion to Markdown.
Any stack that serves semantic HTML works. Hyperkit is the shortest path: author each page once as JSX, and one source serves humans HTML and agents Markdown. The rest of the Hyper Stack is there when you want it: identity, files, and the shared stores.
A complete example
The GitHub Hyperapp fronts GitHub. This page builds its surface. The next two add the rest:
pages/ and api/ are this chapter. connection/ and trigger/ are the
next two.
The root is a Directory Page. Every capability is a link away from it:
const DirectoryPage = () => (
<Layout>
<Directory
title="GitHub"
description="Browse repositories, pull requests, and issues. Read changes and reply. Create issues, PRs, reviews, and files. Open the repo list first, or jump in with ?repo=owner/name."
pages={[
{ href: "/repos", label: "Repositories (all repos in scope)" },
{ href: "/repo?repo=owner/name", label: "One repository (overview, links to code, PRs, issues)" },
{ href: "/pr?repo=owner/name&number=1", label: "Read one PR (description, diff, comments)" },
{ href: "/issues?repo=owner/name", label: "Issues (open issues of a repo)" },
{ href: "/create-issue?repo=owner/name", label: "Create an issue" },
// …the real app lists a dozen pages
]}
/>
</Layout>
)That source renders to one page in two representations:
<html lang="en">
<head>
<meta charset="utf-8" />
<title>GitHub</title>
<meta name="description" content="Browse repositories, pull requests, and issues. Read changes and reply. Create issues, PRs, reviews, and files. Open the repo list first, or jump in with ?repo=owner/name." />
<meta name="hyperapp:type" content="app" />
<meta name="hyperapp:app" content="GitHub" />
<meta name="hyperapp:origin" content="https://github.hyperapp.sh" />
</head>
<body>
<h1>GitHub</h1>
<p>Browse repositories, pull requests, and issues. Read changes and reply. Create issues, PRs, reviews, and files. Open the repo list first, or jump in with ?repo=owner/name.</p>
<h2>Pages</h2>
<ul>
<li><a href="/repos">Repositories (all repos in scope)</a></li>
<li><a href="/repo?repo=owner/name">One repository (overview, links to code, PRs, issues)</a></li>
<li><a href="/pr?repo=owner/name&number=1">Read one PR (description, diff, comments)</a></li>
<li><a href="/issues?repo=owner/name">Issues (open issues of a repo)</a></li>
<li><a href="/create-issue?repo=owner/name">Create an issue</a></li>
</ul>
</body>
</html>---
title: GitHub
description: Browse repositories, pull requests, and issues. Read changes and reply. Create issues, PRs, reviews, and files. Open the repo list first, or jump in with ?repo=owner/name.
hyperapp:type: app
hyperapp:app: GitHub
hyperapp:origin: https://github.hyperapp.sh
---
# GitHub
Browse repositories, pull requests, and issues. Read changes and reply. Create issues, PRs, reviews, and files. Open the repo list first, or jump in with ?repo=owner/name.
## Pages
- [Repositories (all repos in scope)](/repos)
- [One repository (overview, links to code, PRs, issues)](/repo?repo=owner/name)
- [Read one PR (description, diff, comments)](/pr?repo=owner/name&number=1)
- [Issues (open issues of a repo)](/issues?repo=owner/name)
- [Create an issue](/create-issue?repo=owner/name)A capability page renders live state, and its links lead deeper. Reading the page is knowing the repository:
const RepoView = ({ repo }: { repo: Repo }) => (
<Layout>
<Page title={repo.full_name} description={repo.description}>
<h2>Overview</h2>
<ul>
<li>Visibility: {repo.private ? "private" : "public"}</li>
<li>Default branch: {repo.default_branch}</li>
<li>Open issues: {repo.open_issues_count}</li>
</ul>
<h2>Links</h2>
<ul>
<li><a href={`/prs?repo=${repo.full_name}`}>Open pull requests</a></li>
<li><a href={`/issues?repo=${repo.full_name}`}>Open issues</a></li>
</ul>
</Page>
</Layout>
)Rendered:
<html lang="en">
<head>
<meta charset="utf-8" />
<title>agx-computer/hyperapps</title>
<meta name="description" content="Hyperapps for the AGX platform." />
<meta name="hyperapp:type" content="app" />
<meta name="hyperapp:app" content="GitHub" />
<meta name="hyperapp:origin" content="https://github.hyperapp.sh" />
</head>
<body>
<h1>agx-computer/hyperapps</h1>
<p>Hyperapps for the AGX platform.</p>
<h2>Overview</h2>
<ul>
<li>Visibility: public</li>
<li>Default branch: main</li>
<li>Open issues: 3</li>
</ul>
<h2>Links</h2>
<ul>
<li><a href="/prs?repo=agx-computer/hyperapps">Open pull requests</a></li>
<li><a href="/issues?repo=agx-computer/hyperapps">Open issues</a></li>
</ul>
</body>
</html>---
title: agx-computer/hyperapps
description: Hyperapps for the AGX platform.
hyperapp:type: app
hyperapp:app: GitHub
hyperapp:origin: https://github.hyperapp.sh
---
# agx-computer/hyperapps
Hyperapps for the AGX platform.
## Overview
- Visibility: public
- Default branch: main
- Open issues: 3
## Links
- [Open pull requests](/prs?repo=agx-computer/hyperapps)
- [Open issues](/issues?repo=agx-computer/hyperapps)Acting is a form. An Action declares its fields, a human sees a fieldset, an agent sees an Action block, and submitting either returns a Response Page:
<Action name="Reply to this issue" target="/api/issue-comment" submit="Post comment">
<Field name="repo" type="hidden" value={repo} />
<Field name="number" type="hidden" value={String(number)} />
<Field name="body" type="textarea" label="comment (markdown)" rows={6} required />
</Action>Rendered:
<form method="post" name="Reply to this issue" action="/api/issue-comment">
<fieldset>
<legend>Reply to this issue</legend>
<input type="hidden" name="repo" value="agx-computer/hyperapps" />
<input type="hidden" name="number" value="12" />
<p>
<label for="body">comment (markdown)</label><br />
<textarea id="body" name="body" rows="6" required></textarea>
</p>
<button type="submit">Post comment</button>
</fieldset>
</form>## Action: Reply to this issue
- name: `Reply to this issue`
- method: POST
- target: `/api/issue-comment`
### Inputs
| name | type | required | value |
| --- | --- | --- | --- |
| repo | hidden | no | agx-computer/hyperapps |
| number | hidden | no | 12 |
| body | textarea | yes | |Browsing public repositories needs nothing more than these pages. But posting that comment acts on GitHub as someone, and a private repository will not even answer a read. That someone is the next page: adding a connection. And when the repository should call back (a push lands, an issue opens), that is adding a trigger.