Playground

Interactive examples using µJS. Each demo loads real HTML fragments via AJAX — open DevTools (Network tab) to see the requests.

Basic navigation #

A link that replaces a specific zone using mu-target and mu-source. Click the link, then follow the link inside the loaded fragment.

<a href="/demos/fragment-about.html"
   mu-target="#content"
   mu-source="#content"
   mu-history="false" mu-scroll="false">Load About fragment</a>

<div id="content">
    <p>Content will appear here.</p>
</div>

Load About fragment

Content will appear here.

Injection modes #

Append and prepend items to a list using mu-mode.

<a href="/demos/append-item.html"
   mu-target="#demo-list"
   mu-source="#new-item"
   mu-mode="append"
   mu-history="false" mu-scroll="false">Append item</a>

<a href="/demos/prepend-item.html"
   mu-target="#demo-list"
   mu-source="#new-item"
   mu-mode="prepend"
   mu-history="false" mu-scroll="false">Prepend item</a>

Append item   Prepend item

  • Original item 1
  • Original item 2
  • Original item 3

Patch mode #

A single request that updates two different zones simultaneously.

<a href="/demos/patch-response.html"
   mu-mode="patch">Update both zones</a>

<div id="patch-zone-a">Zone A: waiting...</div>
<div id="patch-zone-b">Zone B: waiting...</div>

Update both zones

Zone A: waiting...
Zone B: waiting...

History & Scroll #

Compare normal navigation (adds to browser history) vs mu-history="false" (does not). Watch the URL bar.

<!-- Normal: URL changes -->
<a href="/demos/fragment-about.html"
   mu-target="#history-demo"
   mu-source="#content">Normal link</a>

<!-- No history: URL stays the same -->
<a href="/demos/fragment-contact.html"
   mu-target="#history-demo"
   mu-source="#content"
   mu-history="false">No history link</a>

Normal link (URL changes)   No history link (URL stays)

Watch the browser URL bar when you click each link.

Prefetch #

Hover over the link and watch the Network tab in DevTools — the page is fetched before you click.

<a href="/demos/fragment-about.html"
   mu-target="#prefetch-demo"
   mu-source="#content"
   mu-history="false" mu-scroll="false">Hover me, then click</a>

Hover me, then click

Open DevTools Network tab, then hover over the link above.

Form (GET) #

A search form that loads results via AJAX. The query string is appended to the URL.

<form action="/demos/search-results.html" method="get"
      mu-target="#search-results"
      mu-source="#search-results"
      mu-history="false" mu-scroll="false">
    <input type="text" name="q" placeholder="Search...">
    <button type="submit">Search</button>
</form>

Results will appear here.

Programmatic API #

Click the button to trigger mu.load() from JavaScript — no link needed.

<button onclick="mu.load('/demos/loaded-content.html', {
    target: '#api-demo',
    source: '#api-content',
    history: false,
    scroll: false
})">Load via mu.load()</button>

Content will be loaded programmatically.

Events #

µJS fires custom events during navigation. Click the link below and watch the event log fill up.

document.addEventListener("mu:before-fetch", function(e) {
    console.log("Fetching:", e.detail.url);
});
document.addEventListener("mu:after-render", function() {
    console.log("Page updated");
});

Trigger a navigation

Content will appear here.

Event log

An input field that fetches search results on every keystroke, with a 500ms debounce. Uses mu-trigger="change".

<input type="text" name="q" placeholder="Search..."
       mu-trigger="change" mu-debounce="500"
       mu-url="/demos/trigger-search.html"
       mu-target="#trigger-results" mu-source="#trigger-results"
       mu-mode="update">

<div id="trigger-results">
    <p>Results will appear here.</p>
</div>

Results will appear here.

Triggers — Load on render #

Content is loaded automatically when the element is rendered, using mu-trigger="load".

<div mu-trigger="load"
     mu-url="/demos/loaded-content.html"
     mu-target="#load-demo" mu-source="#api-content"
     mu-mode="update">
</div>

Loading content automatically...

HTTP methods #

Use mu-method to send PUT, PATCH, or DELETE requests from any element.

<!-- DELETE button -->
<button mu-url="/dyn/showMethod/42" mu-method="delete"
        mu-mode="remove" mu-target="#item-42" mu-history="false">
    Delete
</button>

<!-- PUT link -->
<a href="/dyn/showMethod/5" mu-method="put" mu-mode="none" mu-history="false">
    Publish
</a>

Publish

Item to remove

Server-Sent Events (SSE) #

Open a real-time SSE connection with mu-method="sse". Combine with patch mode for multi-fragment streaming.

<!-- Client -->
<div mu-trigger="load" mu-url="/chat/stream"
     mu-mode="patch" mu-method="sse">
</div>

<!-- Server response (SSE format) -->
event: message
data: <div mu-patch-target="#messages" mu-patch-mode="append"><p>New message!</p></div>

event: message
data: <span mu-patch-target="#online-count">42</span>
This example requires a server that supports Server-Sent Events. No live demo available.

Polling #

Combine mu-trigger="load" with mu-repeat to fetch content at regular intervals. The timestamp below updates every 3 seconds.

<div mu-trigger="load" mu-repeat="3000"
     mu-url="/demos/polling-content.html"
     mu-target="#poll-zone" mu-source="#polling-content"
     mu-mode="update">
</div>
<div id="poll-zone">
    Waiting for first poll...
</div>

Waiting for first poll...

Form POST with patch response #

A POST form combined with patch mode to append a new comment and reset the form in a single response.

<!-- Form -->
<form id="comment-form" action="/comment/create"
      method="post" mu-mode="patch">
    <textarea name="body"></textarea>
    <button type="submit">Send</button>
</form>

<div id="comments"></div>

<!-- Server response -->
<div class="comment" mu-patch-target="#comments"
     mu-patch-mode="append">
    <p>The new comment</p>
</div>

<form action="/comment/create" method="post"
      mu-patch-target="#comment-form">
    <textarea name="body"></textarea>
    <button type="submit">Send</button>
</form>
This example requires a dynamic backend. No live demo available.

Confirm dialog #

The mu-confirm attribute shows a browser confirmation dialog before navigating. If the user cancels, navigation is aborted.

<a href="/demos/fragment-about.html"
   mu-target="#confirm-demo" mu-source="#content"
   mu-history="false" mu-scroll="false"
   mu-confirm="Are you sure you want to load this content?">
    Load with confirmation
</a>

Load with confirmation

Content will appear here after confirmation.

View Transitions #

µJS uses the View Transitions API by default. Compare a normal link (with transitions) and one with mu-transition="false".

<!-- With transitions (default) -->
<a href="/demos/fragment-about.html"
   mu-target="#transition-demo" mu-source="#content"
   mu-history="false" mu-scroll="false">With transition</a>

<!-- Without transitions -->
<a href="/demos/fragment-contact.html"
   mu-target="#transition-demo" mu-source="#content"
   mu-history="false" mu-scroll="false" mu-transition="false">Without transition</a>

With transition   Without transition

Click each link to compare the visual effect.

Live validation #

As-you-type email validation using mu-trigger="change" with debounce. DOM morphing (idiomorph) preserves cursor position and focus — the input is never replaced, only the validation message is updated.

<div id="email-wrapper">
    <label for="email-input">Email</label>
    <input type="email" id="email-input" name="email"
           mu-trigger="change" mu-debounce="300"
           mu-url="/validate/email"
           mu-target="#email-wrapper"
           mu-source="#email-wrapper"
           mu-mode="update"
           placeholder="you@example.com">
    <small id="email-error"></small>
</div>

Interdependent fields #

A country selector that updates the city list and postal code format using mu-mode="patch". Only the dependent fields are replaced — the name field (already filled in) stays untouched.

<input type="text" name="name" placeholder="Your name">

<select name="country"
        mu-trigger="change"
        mu-url="/form/update"
        mu-mode="patch">
    <option value="">Choose a country...</option>
    <option value="us">United States</option>
    <option value="fr">France</option>
    <option value="jp">Japan</option>
</select>

<div id="city-wrapper">
    <!-- Updated by the server -->
</div>

<div id="postal-wrapper">
    <!-- Updated by the server -->
</div>