Building a Library with RequireJS

Share this article

RequireJS is an AMD module loader for browsers that can load your script and CSS files asynchronously. You no longer have to deal with the order of script files inside an individual file (e.g. index.html). Instead, you just wrap your code inside module definitions and RequireJS will take care of the dependencies, making your code more structured and well organized. It also has an optimizer tool that uglifies and concatenates the files for production use.

The official site provides extensive documentation about its API, and there are many example repositories to help you. But it has a lot of configuration and it is tricky at first to get started with RequireJS.

In this article we will learn how to use RequireJS by building a library using AMD modules, optimizing it and exporting it as a standalone module using the RequireJS optimizer. Later we will use RequireJS to build an application and consume our library.

This tutorial assumes some familiarity with RequireJS. If you are looking for a primer, check out: Understanding RequireJS for Effective JavaScript Module Loading.

Installing RequireJS

RequireJS is available through bower:

bower install requirejs --save

or you can grab the files on github.

There is also a Grunt-based Yeoman generator for RequireJS projects.

Defining an AMD module

We will wrap our code inside define(), and that will make it an AMD module.

File: mylib.js

define(['jquery'], function($) {
    // $ is jquery now.

    return 'mylib';
});

That’s it. Note that define() takes an optional first argument of a dependency array, in this case it is ['jquery']. It’s the dependency list for this module. All the modules inside the array will be loaded before this module. When this module is executed, the arguments are the corresponding modules in the dependency array.

So in this case jQuery will be loaded first, then passed into the function as parameter $, then we can safely use it inside our module. Finally our module returns a string. The return value is what gets passed to the function parameter when this module is required.

Requiring Other Modules

Let’s see how this works by defining a second module and require our first module mylib.js.

File: main.js

define(['jquery', 'mylib'], function($, mylib) {
    // $ is jquery as usual
    // mylib is the string `mylib` because that's the return value
    // from the first module

    return {
        version: '0.0.1, jQuery version: ' + $.fn.jquery,
        mylibString: mylib
    }
});

You can require as many dependencies as you like inside the dependency array, and all the modules will be available through the function parameters in the same order. In this second module we required the jquery and mylib modules, and simply returned an object, exposing some variables. The user of this library will use this object as your library.

Configuring the RequireJS Optimizer: r.js

You might be wondering, how does RequireJS know what file to load only by looking at the string in dependency array? In our case we provided jquery and mylib as strings, and RequireJS knows where those modules are. mylib is simple enough, it’s mylib.js with .js omitted.

How about jquery? That’s where RequireJS config is used. You can provide extensive configuration through a RequireJS config. There are two ways to provide this config, since we are using the RequireJS optimizer, I will show you the r.js way. r.js is the RequireJS optimizer.

We will provide r.js with a config, and it will optimize all the modules into a single file. The configuration that we provide will make r.js build the modules as a standalone global library that can be used both as an AMD module or as a global export in the browser.

r.js can be run via command line or as a Node module. There is also a Grunt task grunt-requirejs for running the optimizer.

That being said, let’s see what our configuration looks like:

File: tools/build.js

{
  "baseUrl": "../lib",
  "paths": {
    "mylib": "../main"
  },
  "include": ["../tools/almond", "main"],
  "exclude": ["jquery"],
  "out": "../dist/mylib.js"
  "wrap": {
    "startFile": "wrap.start",
    "endFile": "wrap.end"
  }
}

The configuration file is really the meat of RequireJS. Once you understand how these parameters work, you can use RequireJS like a pro.

You can do different things, and tweak your project builds with the configuration file. To learn more about configuration and RequireJS in general I recommend referencing the docs and the wiki. There is also an example configuration file, that demonstrates how to use the build system, so make sure to reference that as well.

Finally we actually run the optimizer. As I said before, you can run it via command line, or Node, as well as a Grunt task. See the r.js README to learn how to run the optimizer in different environments.

node tools/r.js -o tools/build.js

This will generate the build file in dist/mylib.js

build.js

Next, let’s see what the parameters actually mean.

baseUrl – The root path for all module lookups.

paths – Path mappings for module names that are relative to baseUrl.

In our example, “mylib” maps to “../main”, that is relative to baseUrl, so when we require “mylib” it loads the file “../lib/../mylib/main.js”.
Notice we appended baseUrl, then the paths setting, than the module name followed by a .js suffix. That’s where you specify how modules are mapped to files such as jquery and mylib.

include – The modules we want to include in the optimization process. The dependencies that are required by the included modules are included implicitly. In our case, main module depends on mylib and jquery that will get included as well, so no need to include it explicitly. We also include almond that I will mention later.

exclude – The modules we want to exclude from the optimization process. In our case we exclude jquery. The consumers of the built library will provide a jQuery library. We will see that when we consume our library later.

out – The name of the optimized output file.

wrap – Wraps the build bundle in a start and end text specified by wrap. The optimized output file looks like: wrap.start + included modules + wrap.end. wrap.start and wrap.end are the names of the files in which their contents get included in the output.

almond

The built library does not include require.js in the file, but instead uses almond. almond is a small AMD API implementation, that will replace require.js.

Wrapping our Library

In the r.js config we wrapped our library with wrap.start and wrap.end files. We also included almond in our library, those will make our library standalone, so they can be used without RequireJS through browser globals or as an AMD module through requirejs.

File: wrap.start

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD.
    define(['jquery'], factory);
  } else {
    // Browser globals.
    root.mylib = factory(root.$);
  }
}(this, function($) {

Our included modules main, mylib, and almond are here in the middle of wrap.start and wrap.end.

File: wrap.end

// Register in the values from the outer closure for common dependencies
  // as local almond modules
  define('jquery', function() {
    return $;
  });

  // Use almond's special top level synchronous require to trigger factory
  // functions, get the final module, and export it as the public api.
  return require('mylib');
}));

If the consumer uses an AMD loader, then the built file will ask for ‘jquery’ as AMD dependencies. If the consumer just uses browser globals, the library will grab the $ global variable and use it for jQuery dependency.

Using the Library with RequireJS

We are done with our library, now let’s actually use it by building a requirejs application.

File: app.js

define(['jquery', 'mylib'], function($, mylib) {
  // $ is jquery
  // mylib is mylib that is:
  // {
  //   version: 'version 0.0.1 jQuery version: xxx',
  //   mylib: 'mylib'
  // }
});

Nothing fancy here, it’s another module that requires jQuery and mylib. When a module is defined with define it is not immediately executed, that is its callback function (which is passed after the dependency array) is not executed right away. That means our application doesn’t start just by defining this module. Now let’s see how to configure RequireJS and actually execute this module that is our application.

Configuring RequireJS for the Browser

We will configure RequireJS and execute our app module in one file. There are different ways of doing this though.

File: common.js

requirejs.config({
  baseUrl: '../lib',
  paths: {
    'jquery': 'jquery/dist/jquery.min',
    'underscore': 'underscore/dist/underscore',
    'backbone': 'backbone/backbone',
    'mylib': 'mylib/dist/mylib',
    'app': '../app'
  },
  shim: {
    'jquery': {
      exports: '$'
    },
    'backbone': {
      deps: ['jquery', 'underscore'],
      exports: 'Backbone',
    },
    'underscore': {
      exports: '_'
    }
  }
});

require(['app/app'], function(App) {
  // app module is available here
  // you can start your application now
  // this is immediately called because
  // we used `require` instead of `define`
  // to define this module.
});

The baseUrl and paths config are the same as before. The additional config value here is:

shim: Configures the dependencies and exports for traditional “browser globals” scripts that do not use define() to declare the dependencies and set a module value. For example, Backbone is not an AMD module, but it’s a browser global that exports Backbone into the global namespace that we’ve specified in the exports. In our example, the module also depends on jQuery and Underscore, so we specify that using deps. The scripts in the deps array are loaded before Backbone is loaded, and once loaded, the exports value is used as the module value.

Note that you can also use r.js in this application project as well, which will require a separate configuration. But don’t get confused by that. I won’t go into detail on how to do that, but it’s similar to what we did for our library. See the example build config for further reference.

require vs define

Later we use require to load a module and immediately execute it. Sometimes define and require can be confused as to which one is used when. define defines a module, but doesn’t execute it, require defines a module and executes it – that is, it loads and executes the dependent modules before executing itself. Often you will have one require as a main entry module that will depend on additional modules that are defined via define.

Loading the Scripts

Typically you include all your script files in your index.html. Now that we are using RequireJS, we only have to include RequireJS and specify our data-main, which is the entry point to our application. There are different ways of setting up the config options, or separating the main module used in index.html. You can find more information on that here.

<script data-main="scripts/common" src="scripts/lib/require/require.js"></script>

Conclusion

In this article we have built a library, and an application that uses that library with RequireJS. We learned how to configure the r.js optimizer, and how to configure RequireJS in the browser. Finally we learned how to use RequireJS to define and use AMD modules. That made our code well structured and organized.

I’ve used this example-libglobal repo in this tutorial for the first half (configuring the optimizer), and the second half is not that complicated, so you should be good to roll your own now.

The official RequireJS website is the ultimate documentation, but make sure to check out the example repos on github as well the example projects in that repo, which demonstrate the use of a RequireJS application.

Frequently Asked Questions (FAQs) about Building a Library with RequireJS

What is the main purpose of RequireJS in JavaScript development?

RequireJS is a JavaScript file and module loader. It is optimized for in-browser use but can be used in other JavaScript environments as well. The main purpose of RequireJS is to encourage the use of modular programming in JavaScript. It helps developers to manage dependencies between JavaScript files and modularize their code. This leads to better code organization, maintainability, and reusability. It also improves the speed and quality of your code.

How does RequireJS handle JavaScript file dependencies?

RequireJS uses the Asynchronous Module Definition (AMD) API for JavaScript modules. These modules can be loaded asynchronously, which means they don’t block other scripts from running while they load. When you define a module with RequireJS, you specify its dependencies. RequireJS then ensures that these dependencies are loaded before the module itself.

How can I define a module using RequireJS?

To define a module in RequireJS, you use the define() function. This function takes two arguments: an array of dependencies and a factory function. The dependencies are the paths to the files that your module depends on. The factory function is where you write your module code. This function is called once all the dependencies have been loaded.

How can I use a module defined with RequireJS in my code?

To use a module defined with RequireJS, you use the require() function. This function takes two arguments: an array of dependencies and a callback function. The dependencies are the paths to the modules that you want to use. The callback function is where you use the modules. This function is called once all the modules have been loaded.

Can I use RequireJS with other JavaScript libraries like jQuery?

Yes, you can use RequireJS with other JavaScript libraries like jQuery. RequireJS has a built-in feature for loading traditional, non-modular scripts called “shim”. With shim, you can specify dependencies and exports for scripts that do not use define() to declare dependencies and set a module value.

How can I optimize my code with RequireJS?

RequireJS comes with an optimization tool called r.js. This tool combines and minifies your JavaScript files and their dependencies into a single file. This reduces the number of HTTP requests and the size of your files, which can significantly improve the loading time of your web page.

What is the difference between define() and require() in RequireJS?

The define() function is used to define a module, while the require() function is used to load modules. Both functions take an array of dependencies and a function as arguments. However, the function passed to define() is used to create the module value, while the function passed to require() is used to run code after the modules have been loaded.

Can I use RequireJS in Node.js?

Yes, you can use RequireJS in Node.js. However, Node.js has its own module system, so you might not need RequireJS. If you want to use the same code in both the browser and Node.js, or if you prefer the AMD API, then RequireJS can be a good choice.

How can I handle errors in RequireJS?

RequireJS provides an onError callback for handling errors. This callback is called when there is an error loading a module. You can use this callback to log the error or recover from it.

Can I load CSS files with RequireJS?

Yes, you can load CSS files with RequireJS using the require-css plugin. This plugin allows you to load and wait for CSS files, just like you do with JavaScript modules.

Emre GuneylerEmre Guneyler
View Author

Emre Guneyler is a CS student in Turkey. He is passionate about web and game development. Besides programming he enjoys chess, poker, and Call of Duty 4.

javascript modulesLouisLRequireJS
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week