diff --git a/README.md b/README.md
index 5ef74a3c8c303e1a4ab601e7d68d5072b7e12dc2..c84421b102e6be72595e155334f373346a6e17a1 100644
--- a/README.md
+++ b/README.md
@@ -494,7 +494,6 @@ Module declaration, on `config.json`:
     }
 ```
 
-
 ### Job Offers
 
 Job Offers includes a job board with conversation. To activate them
@@ -736,7 +735,7 @@ You need the experimental routing enabled to have a Profile Directory.
 
 ### Add link to left menu
 
-```
+```json
   {
     "type": "link",
     "parameters": {
@@ -749,10 +748,44 @@ You need the experimental routing enabled to have a Profile Directory.
 ```
 
 Where:
+
 * `parameters.icon` is a valid simple line icon
 * `parameters.name` is a path to a translation. You can create your own with the lang component
 * `parameters.target` is the target URL
 
+### Enfore NPM package version
+
+To enforce a version to get used from NPM, add this to your `config.json`:
+
+```json
+  "npm": [
+    {
+      "package": "@startinblox/core",
+      "version": "0"
+    }
+  ],
+```
+
+Where:
+
+* `npm[].package` is the package name to include
+* `npm[].version` if the version of the package you want to use
+* `npm[].attributes` (optional) allows to add attributes to the script element
+
+Package will get required from Skypack CDNs.
+
+#### Use local packages
+
+Use the `npm` parameter on your `config.json`
+
+```json
+    {
+      "package": "@startinblox/oidc",
+      "version": "0",
+      "path": "./path/to/sib-auth/sib-auth.js"
+    }
+```
+
 ### Route generation
 
 Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
diff --git a/internal/assets.js b/internal/assets.js
index 1c239021b96d8f29f983c375ae3d979ae02d168f..a3c564b4d919262d15db88cd23e776cd8eaa1ebf 100644
--- a/internal/assets.js
+++ b/internal/assets.js
@@ -1,8 +1,8 @@
 const HTMLAsset = require('parcel-bundler/lib/assets/HTMLAsset')
 
 function shouldIgnore (file) {
-  // Ignore img(src="${...}") on pug & keep the components folder pristine
-  return /\${.+}/.test(file) || /components/.test(file) || /\/lib\/solid-/.test(file) || /\/lib\/sib-/.test(file);
+  // Ignore img(src="${...}") on pug
+  return /\${.+}/.test(file)
 }
 
 class SkipStartinbloxWidgetAsset extends HTMLAsset {
diff --git a/internal/parcel.js b/internal/parcel.js
index 170ee85f32459870e4b3f2329b8792afacbd09d6..edecff249dffd42b4c997b9834f81b4e1c20850b 100644
--- a/internal/parcel.js
+++ b/internal/parcel.js
@@ -63,9 +63,6 @@ const options = {
   await fse.copy("./src/locales", "./dist/locales")
   console.log(`Copied locales to dist folder`);
 
-  await fse.copy("./src/components", "./dist/components")
-  console.log(`Copied components to dist folder`);
-
   const bundler = new Bundler('./src/index.pug', options);
   bundler.addAssetType('html', require.resolve('./assets.js'));
   if (process.env.NODE_ENV !== 'production') {
diff --git a/src/components/orbit-auto-login.js b/src/components/orbit-auto-login.js
deleted file mode 100644
index c63738842d427b1642240b39f3caa69ff2466772..0000000000000000000000000000000000000000
--- a/src/components/orbit-auto-login.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
-  Sib
-} from 'https://cdn.skypack.dev/@startinblox/core@0.17';
-
-export const OrbitAutoLogin = {
-  name: 'orbit-auto-login',
-  created() {
-    if (window.location.pathname == "/login") {
-      document
-        .querySelectorAll(".loggedIn-loader")
-        .forEach(el => (el.style.display = "flex"));
-      window.dispatchEvent(
-        new CustomEvent('requestNavigation', {
-          detail: {
-            route: window.orbit.getRoute((window.orbit.defaultRoute || "dashboard"), true)
-          }
-        }),
-      );
-      document.querySelector("sib-auth").login();
-    }
-  }
-}
-
-Sib.register(OrbitAutoLogin);
\ No newline at end of file
diff --git a/src/components/orbit-reactivity.js b/src/components/orbit-reactivity.js
deleted file mode 100644
index 5b77308d693846448aacab167f00c17d7bc081e3..0000000000000000000000000000000000000000
--- a/src/components/orbit-reactivity.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import {
-  store,
-  Sib,
-  StoreMixin
-} from 'https://cdn.skypack.dev/@startinblox/core@0.17';
-
-export const OrbitReactivity = {
-  name: 'orbit-reactivity',
-  use: [StoreMixin],
-  attributes: {
-    targetSrc: {
-      type: String,
-      default: '',
-      callback: function () {
-        this.subscribe();
-      }
-    }
-  },
-  async fetchData(value) {
-    this.resourceId = null;
-    if (this.nestedField) {
-      const resource = store.get(value) || await store.getData(value, this.context);
-      const nestedResource = await resource[this.nestedField]
-      this.resourceId = nestedResource ? nestedResource['@id'] : null;
-    } else {
-      this.resourceId = value;
-    }
-    this.subscribe();
-  },
-  unsubscribe(resourceId, targetSrc) {
-    const resourcesSub = store.subscriptionVirtualContainersIndex.get(resourceId);
-    const targetSub = store.subscriptionVirtualContainersIndex.get(targetSrc);
-    const newResourceSub = resourcesSub.filter(r => r !== targetSrc);
-    const newTargetSub = targetSub.filter(r => r !== resourceId);
-    store.subscriptionVirtualContainersIndex.set(resourceId, newResourceSub);
-    store.subscriptionVirtualContainersIndex.set(targetSrc, newTargetSub);
-  },
-  detached() {
-    this.unsubscribe(this.resourceId, this.targetSrc)
-  },
-  subscribe() {
-    if (this.oldResourceId && this.oldTargetSrc && (this.oldResourceId !== this.resourceId || this.oldTargetSrc !== this.targetSrc)) {
-      this.unsubscribe(this.oldResourceId, this.oldTargetSrc);
-    }
-    if (this.resourceId && this.targetSrc) {
-      store.subscribeVirtualContainerTo(this.resourceId, this.targetSrc);
-      store.subscribeVirtualContainerTo(this.targetSrc, this.resourceId);
-      this.oldResourceId = this.resourceId;
-      this.oldTargetSrc = this.targetSrc;
-    }
-  }
-}
-
-Sib.register(OrbitReactivity);
\ No newline at end of file
diff --git a/src/components/sw-toolbox.js b/src/components/sw-toolbox.js
deleted file mode 100644
index 953515796d8e6af0a5252da79f9787583881db6d..0000000000000000000000000000000000000000
--- a/src/components/sw-toolbox.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import {
-  Workbox,
-  messageSW
-} from 'https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-window.prod.mjs';
-
-if ('serviceWorker' in navigator) {
-  window.addEventListener('load', function () {
-
-    const wb = new Workbox('/service-worker.js');
-    let registration;
-
-    const showSkipWaitingPrompt = (event) => {
-      if (orbit.intl.t('serviceWorker.newUpdate') != undefined) {
-        Swal.fire({
-          position: 'bottom-end',
-          backdrop: false,
-          title: "",
-          text: orbit.intl.t('serviceWorker.newUpdate') + ". " + orbit.intl.t('serviceWorker.wantToUpdate'),
-          imageUrl: orbit.client.logo || 'https://cdn.startinblox.com/logos/webp/startinblox.webp',
-          imageAlt: orbit.client.name,
-          showCancelButton: true,
-          confirmButtonClass: 'button text-xsmall text-bold text-center reversed color-secondary bordered icon icon-check icon-margin-right-xsmall no-background-image',
-          cancelButtonClass: 'button text-xsmall text-bold text-center reversed color-primary bordered icon icon-exclamation icon-margin-right-xsmall no-background-image',
-          confirmButtonText: orbit.intl.t('serviceWorker.yes'),
-          cancelButtonText: orbit.intl.t('serviceWorker.no')
-        }).then((result) => {
-          if (result.isConfirmed) {
-            wb.addEventListener('controlling', (event) => {
-              window.location.reload();
-            });
-
-            if (registration && registration.waiting) {
-              messageSW(registration.waiting, {
-                type: 'SKIP_WAITING'
-              });
-            }
-          }
-        });
-      }
-    };
-
-    wb.addEventListener('waiting', showSkipWaitingPrompt);
-    wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
-
-    wb.register().then((r) => registration = r);
-  });
-}
\ No newline at end of file
diff --git a/src/index.pug b/src/index.pug
index 1ac36057c7250affbec31267ef4801e392b07c93..bb4ab8f84e00f4d0d886bcc94ee783c4a07b110c 100644
--- a/src/index.pug
+++ b/src/index.pug
@@ -27,9 +27,6 @@ html(lang="en")
 
     include orbit-envoy.pug
 
-    script(type="module" src="/components/orbit-auto-login.js" defer)
-    script(type="module" src="/components/orbit-reactivity.js" defer)
-    script(type="module" src="/components/sw-toolbox.js" defer)
     script(src="index.js" defer)
 
     include orbit-dependencies.pug
diff --git a/src/libs/sw-toolbox.js b/src/libs/sw-toolbox.js
new file mode 100644
index 0000000000000000000000000000000000000000..df72978ac729bf6327e37f55d3ed02483e72bcfc
--- /dev/null
+++ b/src/libs/sw-toolbox.js
@@ -0,0 +1,44 @@
+import(`https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-window.prod.mjs`).then(workboxWindow => {
+  if ('serviceWorker' in navigator) {
+    window.addEventListener('load', function () {
+
+      const wb = new workboxWindow.Workbox('/service-worker.js');
+      let registration;
+
+      const showSkipWaitingPrompt = (event) => {
+        if (orbit.intl.t('serviceWorker.newUpdate') != undefined) {
+          Swal.fire({
+            position: 'bottom-end',
+            backdrop: false,
+            title: "",
+            text: orbit.intl.t('serviceWorker.newUpdate') + ". " + orbit.intl.t('serviceWorker.wantToUpdate'),
+            imageUrl: orbit.client.logo || 'https://cdn.startinblox.com/logos/webp/startinblox.webp',
+            imageAlt: orbit.client.name,
+            showCancelButton: true,
+            confirmButtonClass: 'button text-xsmall text-bold text-center reversed color-secondary bordered icon icon-check icon-margin-right-xsmall no-background-image',
+            cancelButtonClass: 'button text-xsmall text-bold text-center reversed color-primary bordered icon icon-exclamation icon-margin-right-xsmall no-background-image',
+            confirmButtonText: orbit.intl.t('serviceWorker.yes'),
+            cancelButtonText: orbit.intl.t('serviceWorker.no')
+          }).then((result) => {
+            if (result.isConfirmed) {
+              wb.addEventListener('controlling', (event) => {
+                window.location.reload();
+              });
+
+              if (registration && registration.waiting) {
+                workboxWindow.messageSW(registration.waiting, {
+                  type: 'SKIP_WAITING'
+                });
+              }
+            }
+          });
+        }
+      };
+
+      wb.addEventListener('waiting', showSkipWaitingPrompt);
+      wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
+
+      wb.register().then((r) => registration = r);
+    });
+  }
+});
\ No newline at end of file
diff --git a/src/orbit-dependencies.pug b/src/orbit-dependencies.pug
index 2b47871c2a0139ba8f695a280725289c2d422486..6cd3b9fd1dccee40efa6c8034eae30fa01d65063 100644
--- a/src/orbit-dependencies.pug
+++ b/src/orbit-dependencies.pug
@@ -1,10 +1,7 @@
-script(type="module" src="https://cdn.skypack.dev/@startinblox/core@0.17" defer)
-//- script(type="module" src="/lib/sib-core/dist/index.js" defer)
-
-script(type="module" src="https://cdn.skypack.dev/@startinblox/router@0.11" defer)
-//- script(type="module" src="/lib/sib-router/src/index.js" defer)
-
 -
+  if(!npm) {
+    var npm = [];
+  }
   const componentSet = new Set(components.map(c=>c.type));
   components.map(c => {
     if(c.extensions) {
@@ -12,53 +9,112 @@ script(type="module" src="https://cdn.skypack.dev/@startinblox/router@0.11" defe
     }
   });
 
-if componentSet.has("autoLogin") || componentSet.has("registering")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/oidc@0.15/dist/index.js" defer)
-  //- script(type="module" src="/lib/sib-auth/index.js" defer)
+  //- Managing default versions
+  if(!npm.find(e => e.package == "@startinblox/core"))
+    npm.push({
+      "package": "@startinblox/core",
+      "version": "0.17"
+    })
+
+  if(!npm.find(e => e.package == "@startinblox/router"))
+    npm.push({
+      "package": "@startinblox/router",
+      "version": "0.11"
+    })
 
-if componentSet.has("chat") || componentSet.has("circles") || componentSet.has("projects")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-chat@6.3" defer)
-  //- script(type="module" src="/lib/solid-xmpp-chat/dist/index.js" defer)
+  if(componentSet.has("autoLogin") || componentSet.has("registering"))
+    if(!npm.find(e => e.package == "@startinblox/oidc"))
+      npm.push({
+        "package": "@startinblox/oidc",
+        "version": "0.15"
+      })
 
-if componentSet.has("communities")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/core@0.17/dist/components/solid-map.js" defer)
-  //- script(type="module" src="/lib/sib-core/dist/components/solid-map.js" defer)
+  if(componentSet.has("chat") || componentSet.has("circles") || componentSet.has("projects"))
+    if(!npm.find(e => e.package == "@startinblox/chat"))
+      npm.push({
+        "package": "@startinblox/component-chat",
+        "version": "6.3"
+      })
 
-if componentSet.has("dashboard")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-dashboard@5.1" defer)
-  //- script(type="module" src="/lib/solid-dashboard/dist/index.js" defer)
+  if(componentSet.has("communities"))
+    if(!npm.find(e => e.package == "@startinblox/core" && e.version == "0.17/dist/components/solid-map.js"))
+      npm.push({
+        "package": "@startinblox/core",
+        "version": "0.17/dist/components/solid-map.js"
+      })
 
-if componentSet.has("events")
-  script(type="module", src="https://cdn.skypack.dev/@startinblox/component-event@4.1", defer)
-  //- script(type="module", src="/lib/solid-event/solid-event.js", defer)
+  if(componentSet.has("dashboard"))
+    if(!npm.find(e => e.package == "@startinblox/component-dashboard"))
+      npm.push({
+        "package": "@startinblox/component-dashboard",
+        "version": "5.1"
+      })
 
-if componentSet.has("events") || componentSet.has("polls") || componentSet.has("resources")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-conversation@0.11" defer)
+  if(componentSet.has("events"))
+    if(!npm.find(e => e.package == "@startinblox/component-event"))
+      npm.push({
+        "package": "@startinblox/component-event",
+        "version": "4.1"
+      })
 
-if componentSet.has('invoices')
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-invoicing@1.4" defer)
-  //- script(type="module" src="/lib/solid-invoicing/solid-invoicing.js" defer)
+  if(componentSet.has("events") || componentSet.has("polls") || componentSet.has("resources"))
+    if(!npm.find(e => e.package == "@startinblox/component-conversation"))
+      npm.push({
+        "package": "@startinblox/component-conversation",
+        "version": "0.11"
+      })
 
-if componentSet.has("job-board")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-job-board@6.2" defer)
-  //- script(type="module" src="/lib/solid-job-board/dist/index.js" defer)
+  if(componentSet.has('invoices'))
+    if(!npm.find(e => e.package == "@startinblox/component-invoicing"))
+      npm.push({
+        "package": "@startinblox/component-invoicing",
+        "version": "1.4"
+      })
 
-if componentSet.has("notification")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-notifications@0.13" defer)
-  //- script(type="module" src="/lib/sib-notifications/index.js" defer)
+  if(componentSet.has("job-board"))
+    if(!npm.find(e => e.package == "@startinblox/component-job-board"))
+      npm.push({
+        "package": "@startinblox/component-job-board",
+        "version": "6.2"
+      })
 
-if componentSet.has("polls")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-poll@3.1" defer)
-  //- script(type="module" src="/lib/solid-poll/index.js" defer)
+  if(componentSet.has("notification"))
+    if(!npm.find(e => e.package == "@startinblox/component-notifications"))
+      npm.push({
+        "package": "@startinblox/component-notifications",
+        "version": "0.13"
+      })
 
-if componentSet.has("directory")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-directory@6.1" defer)
-  //- script(type="module" src="/lib/solid-directory/dist/index.js" defer)
+  if(componentSet.has("polls"))
+    if(!npm.find(e => e.package == "@startinblox/component-poll"))
+      npm.push({
+        "package": "@startinblox/component-poll",
+        "version": "3.1"
+      })
 
-if componentSet.has("resources")
-  script(type="module", src="https://cdn.skypack.dev/@startinblox/component-resource@4.0", defer)
-  //- script(type="module" src="/lib/solid-resource/solid-resource.js" defer)
+  if(componentSet.has("directory"))
+    if(!npm.find(e => e.package == "@startinblox/component-directory"))
+      npm.push({
+        "package": "@startinblox/component-directory",
+        "version": "6.1"
+      })
+
+  if(componentSet.has("resources"))
+    if(!npm.find(e => e.package == "@startinblox/component-resource"))
+      npm.push({
+        "package": "@startinblox/component-resource",
+        "version": "4.0"
+      })
+
+for dependency of npm
+  if dependency.path
+    script(type="module" src=dependency.path defer)&attributes(dependency.attributes || {})
+  else
+    script(type="module" src=`https://cdn.skypack.dev/${dependency.package}@${dependency.version}` defer)&attributes(dependency.attributes || {})
 
 if componentSet.has("themeChecker")
   script(src="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js" defer)
   link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css')
+
+- const orbitVersions = `window.orbit.npm = ${JSON.stringify(npm)};`;
+script!=orbitVersions
diff --git a/src/scripts/orbit-auto-login.js b/src/scripts/orbit-auto-login.js
new file mode 100644
index 0000000000000000000000000000000000000000..938a60918fb286c435b288b838f3e3cf6e7a8988
--- /dev/null
+++ b/src/scripts/orbit-auto-login.js
@@ -0,0 +1,21 @@
+let core = orbit.npm.find(e => e.package == "@startinblox/core");
+import(`https://cdn.skypack.dev/@startinblox/core@${core.version}`).then(core => {
+  core.Sib.register({
+    name: 'orbit-auto-login',
+    created() {
+      if (window.location.pathname == "/login") {
+        document
+          .querySelectorAll(".loggedIn-loader")
+          .forEach(el => (el.style.display = "flex"));
+        window.dispatchEvent(
+          new CustomEvent('requestNavigation', {
+            detail: {
+              route: window.orbit.getRoute((window.orbit.defaultRoute || "dashboard"), true)
+            }
+          }),
+        );
+        document.querySelector("sib-auth").login();
+      }
+    }
+  });
+})
\ No newline at end of file
diff --git a/src/scripts/orbit-reactivity.js b/src/scripts/orbit-reactivity.js
new file mode 100644
index 0000000000000000000000000000000000000000..05dd9d15172b7a9c9cc9543cbbec12edfb2394d1
--- /dev/null
+++ b/src/scripts/orbit-reactivity.js
@@ -0,0 +1,49 @@
+let core = orbit.npm.find(e => e.package == "@startinblox/core");
+import(`https://cdn.skypack.dev/@startinblox/core@${core.version}`).then(core => {
+  core.Sib.register({
+    name: 'orbit-reactivity',
+    use: [core.StoreMixin],
+    attributes: {
+      targetSrc: {
+        type: String,
+        default: '',
+        callback: function () {
+          this.subscribe();
+        }
+      }
+    },
+    async fetchData(value) {
+      this.resourceId = null;
+      if (this.nestedField) {
+        const resource = core.store.get(value) || await core.store.getData(value, this.context);
+        const nestedResource = await resource[this.nestedField]
+        this.resourceId = nestedResource ? nestedResource['@id'] : null;
+      } else {
+        this.resourceId = value;
+      }
+      this.subscribe();
+    },
+    unsubscribe(resourceId, targetSrc) {
+      const resourcesSub = core.store.subscriptionVirtualContainersIndex.get(resourceId);
+      const targetSub = core.store.subscriptionVirtualContainersIndex.get(targetSrc);
+      const newResourceSub = resourcesSub.filter(r => r !== targetSrc);
+      const newTargetSub = targetSub.filter(r => r !== resourceId);
+      core.store.subscriptionVirtualContainersIndex.set(resourceId, newResourceSub);
+      core.store.subscriptionVirtualContainersIndex.set(targetSrc, newTargetSub);
+    },
+    detached() {
+      this.unsubscribe(this.resourceId, this.targetSrc)
+    },
+    subscribe() {
+      if (this.oldResourceId && this.oldTargetSrc && (this.oldResourceId !== this.resourceId || this.oldTargetSrc !== this.targetSrc)) {
+        this.unsubscribe(this.oldResourceId, this.oldTargetSrc);
+      }
+      if (this.resourceId && this.targetSrc) {
+        core.store.subscribeVirtualContainerTo(this.resourceId, this.targetSrc);
+        core.store.subscribeVirtualContainerTo(this.targetSrc, this.resourceId);
+        this.oldResourceId = this.resourceId;
+        this.oldTargetSrc = this.targetSrc;
+      }
+    }
+  });
+});
\ No newline at end of file