Skip to content

Embedding in any page

A JOPS app can run inside any page that is not a React component: a plain HTML page, or a page rendered by another server framework (PHP, ASP.NET, Rails, Django, …). The page keeps its own header, content and scripts, and your code decides when the app starts and ends.

This is hosted mode, the same mechanism JopsApp uses in Next.js and the JOPS plugin uses in WordPress. On those platforms, use their integration instead:

  • Inside a Next.js page: use JopsApp (see Next.js).
  • On a WordPress site: use the [jops] shortcode (see WordPress).
  1. Load the library with data-host and no data-app. Nothing starts automatically, and window.JOPS exposes mount() and unmount(). Put the import map before it, with the same URL as the script src:

    <script type="importmap">
    { "imports": { "jops-core": "/lib/jops-core.min.js" } }
    </script>
    <script type="module" src="/lib/jops-core.min.js" data-host></script>
  2. Add the element the app renders into. position: relative is needed by the Router’s slide and fade animations:

    <div id="app" style="position: relative"></div>
  3. Mount the app once jops-core is ready:

    <script type="module">
    const start = () => window.JOPS.mount({ app: "/my-app/App.js", root: document.getElementById("app") });
    window.JOPS ? start() : window.addEventListener("jops:ready", start, { once: true });
    </script>

App.js is the same entry module as in a standalone app: its default export is created, and the root view renders into root instead of <body>.

my-app/App.js
import { View } from "jops-core";
export default class App {
constructor() {
const root = new View();
root.loadLayout("/my-app/layout/main.html").then(() => root.render());
}
}
Function Description
mount({ app, root }) Imports app and creates its default export; the root view renders into root (an HTMLElement) instead of <body>. Returns a promise of the App instance
unmount() Ends the app completely: destroys the Router and every view, empties root, clears Store and EventBus, and restores the history methods JOPS wrapped. Safe to call more than once; mount() can start an app again afterwards
  • jops:ready fires on window once jops-core has fully loaded, so a listener may call mount() directly. If window.JOPS already exists, the library has loaded and you can call mount() at once.

  • Module form. mount and unmount are also exported by the module, for modules running on the same page:

    import { mount, unmount } from "jops-core";
    await mount({ app: "/my-app/App.js", root: document.getElementById("app") });
    unmount();

See the mount() reference and Hosted mode for the concepts behind it.

  • A full page load ends the app anyway.
  • A host that removes the app’s element without a page load, or that navigates with history.pushState(), calls unmount() first.
  • When history moves to another path, the Router ends the app by itself.

Paths depend on where the library comes from.

With the library on the same origin (/lib/jops-core.min.js in both the import map and src), write root-absolute paths. They work on every page of the site:

window.JOPS.mount({ app: "/my-app/App.js", root: document.getElementById("app") });
my-app/layout/main.html
<Router jops>
<Users jops path="/users" src="/my-app/Users.js" layout="/my-app/layout/users.html"></Users>
</Router>

ES module import statements inside your .js files resolve relative to the file as usual; the rules above apply to paths JOPS loads (app, src=, layout=, loadLayout()).

  • One JOPS app per page. Router, Store and EventBus are page-wide singletons.
  • One library URL per page. The import map and the script src must be the same URL, so the page has a single jops-core instance.
  • The host page owns the CSS. In hosted mode setStyle() has no effect. Link the app’s stylesheet from the page or from the app’s layout, and scope every rule under the app’s root class (.my-app .card { … }). The stylesheet applies to the whole page, so avoid global rules such as body, *, :root or bare tag selectors.
  • The page owns the path, JOPS owns the hash (/admin/users#details?id=7).
  • No Android back button integration. It is off in hosted mode.