Bootstrap: The Good Parts

I’ve devised a way of loading JUST the needed CSS and/or JS files from Bootstrap, for each individual and discrete component, thanks to StealJS being awesome.

In the past I’ve felt salty about including an entire CSS framework for an app that only uses bits and pieces of the framework for its UI/UX. You could build custom versions of Bootstrap, but keeping that up-to-date was tedious because you had to keep track of what parts you used or didn’t use in your project. A custom build is cumbersome, and yet another thing to maintain, defeating the purpose of using a third party library.

The ideal solution is obviously to let your components specify the exact parts of Bootstrap’s JS and/or CSS they depend upon, and then just include those parts for that specific component, and that’s what I’ve done in the guide below. One other goal was to be able to edit the compile-time variables provided by Bootstrap to allow specifying custom colors or grid dimensions, which is also accomplished!

Thanks to StealJS, using Bootstrap suddenly becomes much more declarative, and you’re no longer sending down the pipeline cruft or unused bulk that your app doesn’t want or need!

1 Like

Please note that this guide is based on pre-release libraries that are not yet in the npm version of the DoneJS generator, so you will need to make some modifications by hand to the project generated by the DoneJS generator until it is updated sometime in the future.

Update you package.json dependencies to look like:

  "dependencies": {
    "bootstrap": "^4.0.0-alpha.2",
    "can-component": "^3.0.0-pre.8",
    "can-connect": "^0.6.0-pre.4",
    "can-define": "^0.7.4",
    "can-map": "^3.0.0-pre.5",
    "can-map-define": "^3.0.0-pre.2",
    "can-route": "^3.0.0-pre.5",
    "can-route-pushstate": "^3.0.0-pre.3",
    "can-zone": "^0.5.5",
    "done-autorender": "^0.9.0-pre.0",
    "done-component": "^0.6.0-pre.1",
    "done-css": "~2.1.0-pre.0",
    "done-serve": "^0.3.0-pre.0",
    "generator-donejs": "^0.10.0-pre.4",
    "jquery": "~2.2.1",
    "steal": "^0.16.0",
    "steal-stache": "^3.0.0-pre.2",
    "tether": "^1.3.2"
  "devDependencies": {
    "can-fixture": "^0.4.0-pre.2",
    "concurrently": "^2.1.0",
    "documentjs": "^0.4.2",
    "donejs-cli": "0.10.0-pre.0",
    "donejs-deploy": "^0.4.0",
    "funcunit": "~3.0.0",
    "steal-qunit": "^0.1.1",
    "steal-tools": "^0.16.0",
    "testee": "^0.2.4"

Install dependencies:

npm install

Update app.js:

import DefineMap from "can-define/map/";
import route from "can-route";
import "can-route-pushstate";

const AppViewModel = DefineMap.extend({
  route: "string",
  message: {
    value: 'Hello World!',
    serialize: false
  title: {
    value: 'my-app',
    serialize: false

export default AppViewModel;

You can also remove the ! at the ends of any special file imports, like .less! in index.stache.

If you care about the demo or test for the generated bootstrap/tooltip:

  • In src/bootstrap/tooltip.html, change can/view/autorender/ => can-view-autorender/
  • In src/bootstrap/tooltip_test.js, change vm.attr('message') => vm.message

See the complete diff here:

1 Like

Awesome @leoj3n! Thanks for running the bleeding edge.