# @quintype/framework

> Libraries to help build Quintype Node.js apps

Latest version **7.37.4** (published 2026-07-21) · ISC license · 790 weekly downloads

## Install

```sh
npm install @quintype/framework
pnpm add @quintype/framework
yarn add @quintype/framework
bun add @quintype/framework
```

## Health

**Score 55/100 (C)** — status: active.

Positive: no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; no esm support.

## Facts

| | |
|---|---|
| Version | 7.37.4 |
| Published | 2026-07-21 |
| First published | 2017-11-29 |
| Weekly downloads | 790 |
| License | ISC |
| TypeScript types | none |
| Module format | CommonJS |
| Node | ^20.0.0 |
| Dependencies | 32 |
| Unpacked size | 591.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Author | Quintype Developers |
| Maintainers | devops-quintype, sharangj, srajesh636, bindiya, reena07111996, harshie46, mauliksharma, rakshi059, veena.yemmiganur, arunabhthakur94, vishwanath.reddy, nandakishore, mohammedshoaib, saiteja8494, divyaprithvi, manu6010, athul_raja, navya-kedlaya |
| Keywords | quintype |

## Links

- npm: https://www.npmjs.com/package/@quintype/framework
- Repository: https://github.com/quintype/quintype-node-framework
- Homepage: https://github.com/quintype/quintype-node-framework#readme
- Issues: https://github.com/quintype/quintype-node-framework/issues
- npm.io page: https://npm.io/package/@quintype/framework

## Dependencies (32)

- [ejs](https://npm.io/package/ejs.md) ^3.1.6
- [atob](https://npm.io/package/atob.md) ^2.1.2
- [chalk](https://npm.io/package/chalk.md) ^4.1.2
- [react](https://npm.io/package/react.md) ^16.14.0
- [redux](https://npm.io/package/redux.md) ^4.1.1
- [lodash](https://npm.io/package/lodash.md) ^4.17.21
- [morgan](https://npm.io/package/morgan.md) ^1.10.0
- [cluster](https://npm.io/package/cluster.md) ^0.7.7
- [express](https://npm.io/package/express.md) ^4.17.1
- [js-yaml](https://npm.io/package/js-yaml.md) ^4.1.0
- [winston](https://npm.io/package/winston.md) 3.3.3
- [firebase](https://npm.io/package/firebase.md) ^10.6.0
- [react-dom](https://npm.io/package/react-dom.md) ^16.14.0
- [http-proxy](https://npm.io/package/http-proxy.md) ^1.18.1
- [compression](https://npm.io/package/compression.md) ^1.7.4
- [react-redux](https://npm.io/package/react-redux.md) ^7.2.5
- [@jsdoc/salty](https://npm.io/package/@jsdoc/salty.md) ^0.2.9
- [react-router](https://npm.io/package/react-router.md) ^5.2.1
- [@grpc/grpc-js](https://npm.io/package/@grpc/grpc-js.md) ^1.12.5
- [@quintype/amp](https://npm.io/package/@quintype/amp.md) ^2.22.17
- [@quintype/seo](https://npm.io/package/@quintype/seo.md) ^1.53.1
- [sleep-promise](https://npm.io/package/sleep-promise.md) ^9.1.0
- [firebase-admin](https://npm.io/package/firebase-admin.md) ^13.1.0
- [get-youtube-id](https://npm.io/package/get-youtube-id.md) ^1.0.1
- [path-to-regexp](https://npm.io/package/path-to-regexp.md) ^6.2.0
- [mocha-snapshots](https://npm.io/package/mocha-snapshots.md) ^4.2.0
- [request-promise](https://npm.io/package/request-promise.md) ^4.2.6
- [@quintype/backend](https://npm.io/package/@quintype/backend.md) ^2.7.0
- [@quintype/components](https://npm.io/package/@quintype/components.md) ^3.5.0
- [@quintype/prerender-node](https://npm.io/package/@quintype/prerender-node.md) ^3.2.26
- [@ampproject/toolbox-optimizer](https://npm.io/package/@ampproject/toolbox-optimizer.md) 2.8.3
- [babel-plugin-react-css-modules](https://npm.io/package/babel-plugin-react-css-modules.md) ^5.2.6

## Recent versions

- 7.37.4 (latest) — 2026-07-21
- 7.37.4-story-author-seo-fw.1 (beta) — 2026-07-21
- 7.4.0-amp-redirection.0 (amp-redirection) — 2022-05-16
- 7.4.0-upgrade-react.1 (upgrade-react) — 2022-04-22
- 7.4.0-react-18.0 (react-18) — 2022-04-22
- 6.3.0-amp-version.0 (amp-version) — 2021-11-18
- 6.0.2-fix-stale-route-data.1 (stale-route-data-hardcoded) — 2021-09-08
- 6.0.2-fix-stale-route-data.0 (with-new-workbox) — 2021-09-06
- 5.0.6-fix-stale-route-data-random-error.0 (test-cf-stale-if-error) — 2021-08-26
- 5.0.6-insert-svg.1 (insert-svg) — 2021-08-25
- 5.0.6-fix-stale-route-data.4 (cf-cache-control) — 2021-08-24
- 5.0.6-fix-error-management.1 (fix-error-management) — 2021-08-23
- 5.0.6-fix-stale-route-data.3 (stale-route-data) — 2021-08-20
- 5.0.6-home-collection-index.1 (home-collection-index) — 2021-08-19
- 4.16.14-test.0 (test) — 2021-07-16
- … 1767 more at https://npm.io/package/@quintype/framework/versions

## README

# @quintype/framework

This is the framework that powers Malibu.

Please see [Isomorphic Rendering](https://developers.quintype.com/malibu/isomorphic-rendering) for an overview of the architecture of this library.

The Documentation is available here: [https://developers.quintype.com/quintype-node-framework](https://developers.quintype.com/quintype-node-framework)

Please see [malibu](https://github.com/quintype/malibu) for a reference application using this architecture.

Some Topics which are not covered in the documentation (yet), are below

## Implementing a new page

In your server.js, you will notice something like the following

```javascript
isomorphicRoutes(app, {
  generateRoutes: generateRoutes,
  loadData: loadData,
  pickComponent: pickComponent,
});
```

This highlights the three important places to put stuff for an isomorphic app

- Match the route against a `pageType`, typically in `app/server/routes.js` (see the routing section above)
- Load the Data Required for that `pageType`, typically in `app/server/load-data.js`. This returns a promise with required data.
- Render the correct component for that `pageType`, typically in `app/isomorphic/pick-component.js`. This must be a pure component

## Forcing Updates

Since is difficult to force Service Workers to update, there is a provision to do such a thing. Add the following to the correct places. Whenever a change is to be forcefully pushed, update the version in app-version.js. The next AJAX page load via `/route-data.json` will force the service worker to update in the background (and the next refresh will have changes).

Ideally, you will have to push this after purging caches on /shell.html and the service worker.

```javascript
// app/isomorphic/app-version.js

module.exports = 1;

// app/server/app.js
import {isomorphicRoutes} from "@quintype/framework/server/routes";
isomorphicRoutes(app, {
  ...
  appVersion: require("../isomorphic/app-version")
  ...
});

// app/client/app.js
startApp(renderApplication, CUSTOM_REDUCERS, {
  ...
  appVersion: require("../isomorphic/app-version")
  ...
});

// views/js/service-worker.ejs
const REQUIRED_ASSETS = [
  {url: '/shell.html', revision: '<%= appVersion %>'},
  ...
];
```

## Structure of the /route-data.json

The response of the /route-data.json will look like the following:

```javascript
{
  appVersion: 42,
  title: "This is the title of the page",
  // When multi domain support is enabled, this will be the canonical url of the domainSlug.
  // See multi domain support for more information
  currentHostUrl: "https://canonical-root.your-host.com",
  primaryHostUrl: "https://www.your-host.com",
  // your loadData function is responsible for loading this entire data
  data: {
    pageType: "story-page",
    story: {}
  }
}
```

## Multi Domain Support

Multi domain support is achieved by assigning a section to a domain via the domain manager in the editor. Once this is done, add the following entries into your `publisher/config.yml`.

```yaml
# publisher/config.yml
domain_mapping:
  subdomain.my-domain.com: sub
  another.my-domain.com: another
```

Doing this will enable a field called `domainSlug` to be passed to various functions, such as `generateRoutes` and `loadData`. This can be used to load data specific to the domain.

Further, `/route-data.json` will have two more fields at the root level. `domainSlug`, which is the slug of the loaded domain. `currentHostUrl` specifies which domain you are on. The `currentHostUrl` is used by the link field to decide if it should do an ajax navigation or not.

## Debugging

- In order to use `assetify` function, please annotate the application-js with id="app-js". The hostname specified here is assumed to be the cdn
- All code related to the browser loading the service worker can be found in [load-service-worker.js](client/load-service-worker.js)
- All code related to the service worker itself is found in [service-worker-helper.js](client/service-worker-helper.js)

## Miscellaneous

### Switching to generateCommonRoutes

Starting in `@quintype/framework` v3, we are introducing multi domain support. The configuration for section page locations is now controlled by the editor. Please switch to using `generateCommonRoutes` to generate story page, and section page routes.

```javascript
import { generateCommonRoutes } from "@quintype/framework/server/generate-routes";

export function generateRoutes(config, domainSlug) {
  return generateCommonRoutes(config, domainSlug, {
    sectionPageRoutes: true,
    storyPageRoutes: true,
  });
}
```

### OneSignal Integration

OneSignal interferes with our service worker, so a few changes have to be made to enable PWA with OneSignal.

```javascript
// app/server/app.js

import {isomorphicRoutes} from "@quintype/framework/server/routes";
isomorphicRoutes(app, {
  ...
  oneSignalServiceWorkers: true
  ...
});

// app/client/app.js

startApp(renderApplication, CUSTOM_REDUCERS, {
  enableServiceWorker: true,
  serviceWorkerLocation: "/OneSignalSDKWorker.js", // OneSignal will automatically register the service worker
})
```

### FCM Integration

Steps to Integrate FCM in your project

1. app/client/app.js While executing startApp in your project send the firebaseConfig via opts. `firebaseConfig` can either be an object or a function that returns the firebase config object

Example:

```js
startApp(renderApplication,
  CUSTOM_REDUCERS,
  {
  enableServiceWorker: process.env.NODE_ENV === "production",
  firebaseConfig: {
    messagingSenderId: --YOUR KEY-- ,
    projectId: --YOUR KEY--,
    apiKey: --YOUR KEY--,
    storageBucket: --YOUR KEY--,
    authDomain: --YOUR KEY--,
    appId: --YOUR KEY--,
  }
  ...
})
```

2. app/server/app.js should have the fcm configuration as below:

```js
isomorphicRoutes(app, {
  ...
  fcmServiceCreds: (config) => <ServiceCreds> || fcmServiceCreds: <ServiceCreds> {(function|object)}
  ...
});

```

3. You should have service worker script named firebase-messaging-sw.js in /public

   Example of the script:

   ```js
    importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-app-compat.js");
    importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-messaging-compat.js");

    firebase.initializeApp({
      messagingSenderId: --YOUR KEY-- ,
      projectId: --YOUR KEY--,
      apiKey: --YOUR KEY--,
      storageBucket: --YOUR KEY--,
      authDomain: --YOUR KEY--,
      appId: --YOUR KEY--,
    });
    self.addEventListener('notificationclick', (event) => {

      const url = event.notification.data.url;
      if(url) {
        clients.openWindow(url);
      }
      clients.openWindow(-- YOUR Sketches Host --);
    }, false);

    const messaging = firebase.messaging();

    messaging.onBackgroundMessage(function(payload) {
      const data = payload["data"];
      const notificationTitle = data.title || ""
      const notificationOptions = {
        body: data.body,
        icon: data["hero_image_s3_url"],
        image: data["hero_image_s3_url"],
        data: {
          url: data["click_action"],
        }
      };

      self.registration.showNotification(notificationTitle,
        notificationOptions);
    });

   ```

4. Make sure that the page data should have config with key fcmMessageSenderId refer doStartApp function in app/client/start.js.

### Skipping loading data from /route-data.json

This can be used where `/route-data.json` is not accessible (example preview).

Add the following:

```html
<script type="text/javascript">
  var staticPageStoreContent = <%- JSON.stringify(store.getState()) -%>;
</script>
```

The store will be initialized from staticPageStoreContent

### Using Assets in JS and CSS

(to be documented)

## Minimizing Page Load Speed

Make sure you do all of the following techniques to reduce page load time (notes to document these later)

### Inline CSS

### Add a window.initialFetch to do a fetch in the background

### Add a initial-page to preRender chrome such as the menu without waiting for AJAX responses

```html
<script type="application/json" id="initial-page">
  { "config": {} }
</script>
```

### Use static-page to show a full page (this will prevent fetchData from calling)

```html
<script type="application/json" id="static-page">
  { "config": {} }
</script>
```

### Never require lodash directly. Always do lodash/get

### Do not use moment. Use date-fns

### LazyLoad Images

### Separate polyfills

### Use Link headers to do HTTP2 server push to prioritize important requests

Preloading app.js and /route-data.json can be triggered by passing preloadJS true, and preloadRouteData true to isomorphic handler

## Options

### manifest-fn

### forwardAmp

### multiple publishers

FIXME: Write notes on `host_to_api_host`, `host_to_automatic_api_host`, `wildcard_to_api_host` and `skip_warm_config`

### forwardFavicon

## Optimising front-end javascript

1. https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking/

## Migration to framework@3

- Run the following to execute the script:

```sh
sh <(curl https://raw.githubusercontent.com/quintype/quintype-node-framework/master/scripts/framework-2-to-3-migration)
```

- Verify Changes with `git diff --cached`

## References

- This architecture is heavily influenced by the method described in this [video](https://www.youtube.com/watch?v=atUdVSuNRjA)
- Code for the available video is available [here](https://github.com/gja/pwa-clojure)
- I know there is a good tutorial video I've seen. But I can't remember where.
- Great [intro to pwa](https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/)

---
_Source: https://npm.io/package/@quintype/framework · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
