git push. Then actually ship it.
Point Shipped at any public GitHub repository. It pulls the commits from the latest release, you curate them into human language, and it renders the release email, the public changelog page, and a print-ready PDF digest — live, in your browser.
Built with Unlayer Elements.
#BuiltWithElements
Your users do not read commits. They read release notes — and the gap between the two is why most projects either ship a raw git log or nothing at all.
Shipped closes that gap without a backend, an account, or an API key. It is not a template with sample data: it works on your repository, today.
- Paste a repo —
owner/repoor a GitHub URL. Shipped calls the GitHub API from the browser (2–3 requests, no auth): the repo, its latest releases, and the commit range between the last two tags — exactly what shipped in that release. Repos without releases fall back to the recent commit stream. - Curate the queue — commits arrive pre-classified from their conventional-commit
prefixes (
feat:→ feature,!→ breaking, and keyword fallbacks for repos that do not use prefixes). You tick what ships, rewrite titles into human language, retype anything the classifier got wrong, and write a two-line intro. Chores start unticked. - Ship every format — the same curated release renders as three artifacts. Your curation persists in localStorage per repo, and a re-fetch merges your edits over the new commits by sha, so pulling again never destroys your work.
| Artifact | Mode | What it is |
|---|---|---|
| Release email | <Email> |
Outlook-safe tables, plain-text part included |
| Changelog page | <Page> |
Responsive public changelog |
| PDF digest | <Document> |
Print-tuned A4 with page-break rules |
The right panel is a block composer. Every artifact is an ordered list of document blocks — version heading, composition bar, intro, changelog, contributors, CTA — and you can:
- Drag blocks to reorder them — pointer-events + FLIP animation, no drag library. Displaced rows glide out of the way; on drop, nothing jumps.
- Toggle any block off — it disappears from all three artifacts at once.
- Restyle everything live: accent colour (swatches or hex), sans/serif headings, cozy/compact density, corner radius, and whether commit metadata shows.
- Add a note to any change — a longer description rendered under the title in every artifact, not just the studio.
All of it persists per repo and applies to the email, the page, and the PDF simultaneously, because they are one component tree.
The three artifact tabs are not three templates — they are one component tree rendered through three root wrappers. That is Unlayer Elements' entire pitch, and here it runs live on real repository data.
Three custom tools, each registered with registerTool() and shipping per-mode exporters:
- Composition bar — what the release is made of, as a proportional segmented bar. The
email exporter emits one
<td>per segment with percentage widths, because Outlook will not honour flex and rounds styled div widths. - Type badges — the feature/fix/breaking labels, table-wrapped for email.
- Contributor strip — GitHub avatars in a row; plain
<img>in table cells so they load in email and print alike.
registerTool({
name: 'shipped-composition',
renderer: {
exporters: {
email: (v) => /* <td> per segment, % widths */,
web: (v) => /* flex row */,
},
},
});| Entry | Curating unlayer/elements |
|---|---|
![]() |
![]() |
Static renders of the bundled sample (a real snapshot of unlayer/elements v0.1.20):
| Changelog | Digest | |
|---|---|---|
![]() |
![]() |
![]() |
git clone https://github.com/rohit-jsfreaky/shipped
cd shipped
npm install
npm run dev| Command | What it does |
|---|---|
npm run dev |
Landing + studio — start here |
npm run build |
Static build to dist/ (deploys anywhere) |
npm run render |
Static artifacts from the bundled sample → out/ |
npm run pdf |
Sample digest → real PDF via Chrome |
npm run shots |
Desktop + mobile screenshots |
npm run typecheck |
TypeScript check |
Everything is client-side: @unlayer/react-elements imports no Node builtins, and
api.github.com sends CORS headers. The unauthenticated GitHub limit is 60 requests/hour
per IP; a full fetch costs 2–3, and rate-limit errors are surfaced honestly.
src/
data/
types.ts the Release/Change contract
sample.ts real snapshot of unlayer/elements v0.1.20
lib/
github.ts browser GitHub client + conventional-commit classifier
store.ts localStorage persistence + curation merge
theme.ts artifact palette (light) + GitHub-label type ramp
layout.ts mode-aware measure + root wrapper selection
sections.tsx shared Rows: masthead, grouped changes, labels
tools/
badge.ts custom tool — type badges
composition.ts custom tool — proportional composition bar
avatars.ts custom tool — contributor strip
templates/ the three artifacts + registry
ui/
icons.tsx hand-drawn 16×16 SVG icon set (no icon library)
kit.tsx reusable buttons, eyebrow, panel, fields, custom select
studio/ entry screen + curation workbench (dark, editorial)
landing/ the landing page
scripts/ render / pdf / screenshots
- Emails are table-based with MSO conditionals but have not been run through Litmus — test in a real inbox before wiring this into a sender.
- The classifier is heuristics on commit messages; that is why the curation step exists.
- Two
@unlayer/react-elements0.1.20 gotchas are inherited from earlier builds in this series and documented insrc/lib/theme.ts(noStackMobilenot wired up; per-Row<style>blocks overriding.no-stack— fixed with a higher-specificity rule). A third: section helpers must return<Row>arrays, never fragments —renderToJsonrejects fragments as Body children.
Built with @unlayer/react-elements (MIT) by
Unlayer. MIT licensed.







