afetch is minimal JavaScript library for adding fetch() capabilities to<a> and <button> tags. afetch turns plain HTML into real apps: trigger fetch() with declarative attributes on <a> and <button>. No framework. No build step. Works anywhere.
- This
<a>does not usehref. Instead, thefetchattribute turns the link into a fetch trigger. No page navigation happens. fetch="/api/hello"tells aFetch what URL to request when the link is activated (clicked).- When the response is JSON, aFetch calls the
fetch-onjsonhook with an object that includes the parsed JSON asdata. - In the example, the hook destructures
{ data }and showsdata.messagein an alert.
<script src="afetch.js"></script>
<!-- For CDN: https://cdn.jsdelivr.net/gh/naveedurrehman/afetch/dist/afetch.min.js -->
<a
fetch="/api/hello"
fetch-onjson="({data}) => alert(data.message)"
>
Click Me!
</a>So: click → aFetch does GET /api/hello → response is parsed as JSON → your fetch-onjson handler runs with { data }.
The example above is equivalent to the following, longer code using the browser’s native fetch() API. Notice how much more concise the afetch version is!
<-- Longer and much complex codes without afetch! :( -->
<a id="hello-link">Click Me!</a>
<script>
document.getElementById('hello-link').addEventListener('click', async (e) => {
e.preventDefault();
try {
const res = await fetch('/api/hello', { headers: { 'Accept': 'application/json' } });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
alert(data.message);
} catch (err) {
console.error(err);
// optional: alert('Something went wrong')
}
});
</script>Try the following Demo at CodePen:
A collection of runnable afetch examples is available on CodePen. Browse it here: CodePen collection.
- Example 1: Basic
- Example 2: Raw Data
- Example 3: Form Data
- Example 4: Upload File
- Example 5: Hooks Basic
- Example 6: Hooks for Parsed Data
- Example 7: Handling Errors
- Example 8: Handling Timeout
- Example 9: No Cookies
- Example 10: Dynamic
- Example 11: Spinner
- Example 12: Redirects
- Example 13: Placeholders
- Progressive enhancement, zero boilerplate. Add a couple of
fetch-*attributes to links or buttons and you’ve got async behavior—no framework, no controllers, no bundler required. - Placeholders. Use {} in fetch url; at request time afetch resolves each to the matched element’s best value (value/text/selected/etc.).
- Predictable parsing.
fetch-response="auto"smart-parses by content type (JSON / text / blob / js), or you can force a mode per request for consistency across endpoints. - Drop-in rendering. Point at any selector with
fetch-targetand choose how content lands (fetch-target-format,fetch-target-mode), so you can preview responses without writing DOM code. - Great with forms. Use
fetch-formsto collect fields (even across multiple forms) and optionally merge JSON viafetch-body. GET bodies become query params automatically. - Hooks everywhere. From
fetch-onbeforeandfetch-onstarttofetch-onjson,fetch-onfailure,fetch-ontimeout, andfetch-oncomplete, you get clear, composable extension points—perfect for logging, analytics, and UI state. - UX niceties built-in.
fetch-spinnertoggles spinners for you;fetch-disablingprevents double clicks; timeouts useAbortControllerfor responsive UIs. - A11y-friendly defaults. Upgraded anchors get
role="button"andtabindex="0", and controls setaria-disabledduring requests. - Fetch-native. Most
fetch-*attributes map straight to the standardwindow.fetchinit (method, headers, credentials, cache, redirect, referrer, referrer-policy), so it plays nicely with your existing APIs. - Framework-agnostic. Works in plain HTML or alongside React/Vue/Svelte/Rails/Django/Laravel—you’re just enhancing markup.
- Easy to scale. New elements added later are auto-observed.
tl;dr — afetch gives you the 80% you write over and over (wiring, parsing, rendering, and states) as HTML attributes and well-placed hooks, so you ship interactivity faster with less JavaScript.
| Attribute | Type | Default | Possible values / notes | Effect |
|---|---|---|---|---|
fetch |
string (URL) | — | Required. Relative or absolute URL. | Endpoint to request when the element is activated. |
fetch-method |
string | GET |
Any valid fetch() method (e.g., GET, POST, …). If used with fetch-forms and you set GET/HEAD, the library auto-switches to POST. |
HTTP method. |
fetch-mode / fetch-credentials / fetch-cache / fetch-redirect / fetch-referrer / fetch-referrer-policy |
string | — | Pass-through to window.fetch init. If not set, omitted. |
Controls lower-level fetch behavior. |
fetch-headers |
JSON | — | Object of header key/values; merged into a Headers instance. |
Additional request headers. |
fetch-body |
JSON | — | For GET/HEAD, becomes query params; otherwise serialized. If Content-Type is exactly application/x-www-form-urlencoded, it’s URL-encoded; otherwise defaults to JSON and sets Content-Type: application/json. |
Request payload. |
fetch-forms |
CSV (form IDs) | — | Collects fields from the listed forms into FormData. If fetch-body is also present, its JSON is merged into that FormData. Using GET/HEAD forces a POST. |
Builds a FormData body from existing forms. |
fetch-response |
string | auto |
auto (sniff content-type), json, text, blob. JavaScript (application/javascript / text/javascript) is parsed as js. |
Selects how the response is parsed before hooks run. |
fetch-execjs |
boolean | true |
Parsed with tolerant boolean tokens: true/false, 1/0, yes/no, on/off, or presence-only. When the parsed kind is js and this is true, the script is executed. |
Auto-executes JavaScript response bodies. |
fetch-timeout |
number (ms) | 0 (no timeout) |
Any integer ≥ 0. On timeout, request is aborted and fetch-ontimeout fires. |
Aborts long-running requests. |
fetch-target |
CSS selector | null |
Any valid selector. If set, response preview is rendered there. | Where to render the response (preview). |
fetch-target-format |
string | html |
html → uses innerHTML, anything else → textContent. |
How the preview is inserted. |
fetch-target-mode |
string/number | 0 (reset) |
reset/0, append/1, prepend/-1. |
How the preview is positioned relative to existing content. |
fetch-spinner |
CSS selector | — | Elements matching the selector are hidden initially and shown during the request; hidden again on completion. | Simple show/hide spinner control. |
fetch-disabling |
(presence-only) | — | If present, the clicked control is .disabled = true during the request and restored afterwards. (Regardless, aria-disabled is set while active for accessibility.) |
Prevents double submits. |
| Hook attribute | When it fires | detail payload | Notes / common use |
|---|---|---|---|
fetch-onbefore |
Right before reading attributes and starting the request | the element itself (<a>/<button>) |
Last chance to tweak the DOM/state. |
fetch-onstart |
After options are prepared; just before calling fetch() |
{ element, url, method, init: { headers, body } } |
Great for logging/telemetry. |
fetch-onresponse |
Immediately after fetch() resolves (even if not OK) |
{ element, url, method, response } |
Inspect status/headers early. |
fetch-onfailure |
On network error or non-OK responses | For non-OK: { element, url, method, error: parsed }; on exceptions: { element, url, method, error } |
Fired alongside fetch-onerror. |
fetch-onparsingerror |
Response OK but parsing failed | { element, url, method, error, raw, response } |
You get raw best-effort text. |
fetch-onjson / fetch-ontext / fetch-onblob / fetch-onjs |
After successful parse | { element, url, method, data, response } |
For onjs, if fetch-execjs is true, the JS is also executed. |
fetch-ontimeout |
When fetch-timeout aborts the request |
{ element, url, method } |
Triggered by an AbortController. |
fetch-onfetchbodyerror |
If building/merging fetch-body fails up-front |
{ element, error } |
Useful for surfacing invalid JSON before sending. |
fetch-onerror |
Generic error companion | { element, error } |
Emitted alongside specific error hooks for convenience. |
fetch-oncomplete |
Always, after success or failure | { element, url, method } |
Final cleanup; spinner/disabling already reset. |
graph TD
A[User click or keypress] --> B[fetch-onbefore]
B --> C[Read attributes and build request]
C --> D[fetch-onstart]
D --> E{fetch}
E -->|network error| F[fetch-onfailure + fetch-onerror]
E -->|response| G[fetch-onresponse]
G --> H{response ok?}
H -->|no| F
H -->|yes| I[Parse: auto/json/text/blob/js]
I -->|parse error| J[fetch-onparsingerror]
I -->|ok| K{kind}
K -->|json| L[fetch-onjson]
K -->|text| M[fetch-ontext]
K -->|blob| N[fetch-onblob]
K -->|js| O[fetch-onjs; exec if fetch-execjs=true]
L --> P[Render to fetch-target optional]
M --> P
N --> P
O --> P
P --> Q[fetch-oncomplete]
F --> Q
D --> T[timeout watcher]
T -->|elapsed| U[fetch-ontimeout]
U --> Q

