Project structure
A JOPS web project is a folder of static files. There is no build output to manage while you develop: the browser loads exactly the files you see.
Empty template
Section titled “Empty template”Choice 1 in npx jops-core init creates the smallest possible app:
Directorymy-jops-app/
- index.html page with the import map and the module script
Directorylib/
- jops-core.min.js the library, copied from node_modules
Directorysrc/
- App.js entry point
Directorylayout/
- mainlayout.html the root layout
Directory.vscode/
- extensions.json
- settings.json HTML highlighting in layout templates
- package.json with build and postinstall scripts
Directorynode_modules/
- …
The generated index.html looks like this:
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>JOPS App</title> <script type="importmap"> { "imports": { "jops-core": "/lib/jops-core.min.js" } } </script> </head> <body> <script type="module" src="/lib/jops-core.min.js" data-app="./src/App.js"></script> </body></html>The library is served from your own project (/lib/jops-core.min.js), not from a CDN, so the app works offline and always uses the version you installed.
Hello World template
Section titled “Hello World template”Choice 2 adds a complete example app on top of the same base:
Directorymy-jops-app/
- index.html
Directorylib/
- jops-core.min.js
Directoryassets/
- jops-hierarchy.svg
- jops-lifecycle.svg
- jops-navigation.svg
Directorysrc/
- App.js entry point: loads the styles and the main layout
- Features.js route view, adds Card sub-views in code
- Form.js route view with an HTML layout file
- DataSection.js route view, builds table rows and list items
- Media.js route view
Directorycss/
- styles.css
Directorylayout/
- mainlayout.html header, Router with five routes, footer
- layout_header.html
- layout_hero.html
- layout_form.html
- layout_data.html
- layout_footer.html
Directory.vscode/
- …
- package.json
The building blocks
Section titled “The building blocks”index.html
Section titled “index.html”The page the browser opens. It holds the import map (so modules can import … from "jops-core") and the module script whose data-app names your entry point. See Installation.
App.js, the entry point
Section titled “App.js, the entry point”JOPS imports the module named in data-app and creates an instance of its default export. Its job is to create the root view, give it a layout and call render():
import { View } from "jops-core";
export default class App { constructor() { console.log("JOPS App mounted");
const mainContainer = new View(); mainContainer.setStyle("/src/css/styles.css");
mainContainer .loadLayout("/src/layout/mainlayout.html") .then(() => mainContainer.render()); }}The root view renders into <body>, or into the element named by data-root.
Layouts
Section titled “Layouts”A layout is plain HTML: an .html file loaded with loadLayout() or a layout attribute, or a layout template string in a view class. Layouts declare nested views with tags that carry the jops attribute:
<div class="jops-root"> <View jops layout="/src/layout/layout_header.html"></View>
<main class="main"> <Router jops animation="slide"> <View path="/home" jops layout="/src/layout/layout_hero.html"></View> <Features path="/features" jops src="/src/Features.js"></Features> <DataSection path="/data" jops src="/src/DataSection.js"></DataSection> </Router> </main>
<View jops layout="/src/layout/layout_footer.html"></View></div>layout="…"alone creates a plainViewwith that HTML: good for static parts like a header or footer.src="…"loads a view class from a module (its default export).path="…"on a child of<Router>makes the view a route (#home,#features).
A view is a class that extends View. It owns its layout, reacts to lifecycle calls (onLayout(), onResume(), onPause(), onBackPressed()) and handles its events in methods:
import { View, Store } from "jops-core";
export default class Form extends View { async loadLayout() { await super.loadLayout("/src/layout/layout_form.html"); }
onSubmit(event) { event.preventDefault(); const data = Object.fromEntries(new FormData(event.target)); Store.singleton().set("form.submission", data); }}Its layout binds the form’s submit event to onSubmit():
<form class="form" jops-event-bind onsubmit="onSubmit"> …</form>Other project types
Section titled “Other project types”The Next.js (choice 3) and Electron (choice 4) templates create full projects with their own layout. They are described on their platform pages: