

When we shipped hybrid mobile support in JOPS 1.0.13, we said the next step was to examine how JOPS and Next.js could work together. With jops-core 1.1.0, that work is done: a complete JOPS single-page app can now run inside a page of a Next.js app, next to React, with Next.js Route Handlers as its backend.
This isn't JOPS competing with React. Next.js keeps everything it does best: server rendering, data fetching, routing and the React ecosystem. JOPS adds class-based views with an Android-style lifecycle on the pages that need them. One codebase, two models, each where it fits.
Object-oriented design is how most backend and mobile developers think: Java, C#, Kotlin, Swift, Android and UIKit are all built around classes. Modern React has moved the other way. Server Components are async functions, and React's newest features are built for function components. Classes still work in client components, but a class-based architecture no longer fits naturally in a modern React and Next.js codebase.
Functions and hooks are a proven model, and many teams are productive with them. JOPS gives teams that think in classes a first-class place inside the same app, without asking anyone to switch.
The split is simple:
/quotes is a Next.js route; /quotes#details?id=q2 is a screen inside the JOPS app.public/ and is served as-is. Turbopack and webpack never bundle it, so JOPS keeps its no-build design.fetch(), on the same origin, with the session cookie sent automatically.The bridge is a single React component, JopsApp, imported from jops-core/nextjs:
// src/app/quotes/page.tsx
import JopsApp from "jops-core/nextjs";
export default function QuotesPage() {
return <JopsApp app="/quotes/App.js" id="quotes-root" />;
}
JopsApp is a Client Component, so a Server Component page can render it. On the server it renders an empty <div>. In the browser it loads the JOPS library once per page, adds the import map and starts the app inside that <div>. The app's entry module is the same App.js a standalone JOPS app uses:
// public/quotes/App.js
import { View } from "jops-core";
export default class App {
constructor() {
const root = new View();
root.loadLayout("/quotes/layout/main.html").then(() => root.render());
}
}
We didn't design the bridge on a whiteboard. We built a typical Next.js CRM first, on Next.js 16, React 19, the App Router and Tailwind CSS 4, and then rebuilt its entire Quote Builder as a JOPS app inside it:
All of it runs on the CRM's REST Route Handlers, styled with the same Tailwind classes as the rest of the app. Building a real feature end to end exposed issues no demo would have. We fixed them in the framework before the release, and the few that are better handled by convention are listed below.
A JOPS app inside Next.js behaves like an app on a phone:
Store and EventBus, and JOPS's history hooks are all removed. Nothing keeps running in the background.The quickest start is the new project template. In an empty folder:
npx jops-core init
# choose: 3. Next.js JOPS Embedded SPA
npm run dev
It creates a Next.js project with a React-only page, a page hosting a JOPS SPA (a mock login and a quotes list), and a Route Handler as its backend. To add JOPS to an existing Next.js app instead:
npm i jops-core
npx jops-core update # copies the library to public/lib/
Then put your JOPS app in public/ and render <JopsApp app="/my-app/App.js" /> on the page that hosts it.
Not using React? The same mechanism works on any page: load the library with data-host, and start or stop the app yourself with window.JOPS.mount() and unmount().
Building a full application inside Next.js shaped several additions that every JOPS app can use, standalone or hosted:
onPause(), the counterpart of onResume(), called when another view comes to the front while this one stays alive.<Card jops title="Hi" item-id="7">, arrive as read-only this.props.find(id) for every element with a given id.EventBus.subscribe(owner, event, callback) removes itself when its view is destroyed.A few conventions keep the two frameworks out of each other's way:
Store and EventBus are page-wide.public/lib/ rather than a CDN.setStyle() is ignored; Tailwind picks up classes used in your JOPS files.public/ with the app, and use short cache headers for the JOPS files there, since they aren't content-hashed.JOPS now runs in the browser, in hybrid mobile apps and inside Next.js. The next environment on our list is the desktop. The JOPS team will examine integration with Electron, so the same class-based views can power standalone applications on Windows, macOS and Linux, with access to the file system and native menus through Electron's main process. Many of the internal tools JOPS was built for live on the desktop, and we'll share what we learn as the work progresses.
JOPS is built and maintained at UIDB, a boutique team of senior engineers who build custom systems with our clients, side by side. If you're extending a Next.js system or planning one that needs to hold up for years, we'd be glad to talk.
השאירו תגובה