curl.js: Incredible AMD Loader

By on  

Today there are dozens of AMD JavaScript loaders available, the most popular being RequireJS. There are also lesser known JavaScript loaders like YepNope, $script.js, LABjs, and Dojo's new native loader. My favorite JavaScript loader, however, is John Hann (unscriptable)'s curl. While allowing for maximum configuration and reliable loading, curl also allows for loading of simple JavaScript files as well as CSS files. Let me show you how to use it!

Super Quick AMD Primer

If you aren't familiar with AMD structure, I'm going to give you the most oversimplified explanation you'll ever hear. AMD is a system by which you define and require modules asynchronously. A define returns one or zero objects. The first argument of both define and require is (usually) an array of dependencies. The second argument is a function; the define returns the result, the require executes a basic callback:

// "define" a module
define(["namespace/dependencyA", "namespace/dependencyB"], function(depA, depB) {
	// Whole bunch of processing
	// Return what this module defines
	return function() {
		// Or an object, or whatever

// "require" to use modules:
require(["namespace/dependencyC"], function(depC) {
	// depC can be used in here only
	// Yay for modularity!

The slashes in the dependency array items represent paths to module JavaScript files. Once dependencies are loaded, the action is allowed to begin.

As I said, this is a very simple, vanilla example; there are exceptions to every rule, so don't bother pointing out what-ifs.

Configuring Module Loading with curl

And of course I start out with a few of the exceptions to the rule. Instead of a require function, curl.js defines curl in its place. Additionally, curl.js allows for an object literal as a first parameter, allowing for configuration of loaded modules:

		baseUrl: "/path/to/js",
		pluginPath: "curl/src/curl/plugin"
	["namespace/depC", "namespace/otherDep"],
	function(depC, otherDep) {
		// Do stuff

This configuration allows you to provide plugin paths, modules paths, and more.

Basic define and require with curl.js

Basic usage of curl.js is as you would expect from a JavaScript loader; dependency array as the first argument, callback with the second:

define(["namespace/depA", "namespace/depB"], function(depA, depB) {
	// Do something with the dependencies
	// Pump out a return obj
	return myFinalObject;

With a module defined, the same syntax requires and works with the dependencies:

curl(["namespace/depC"], function(depC) {
	// Do some stuff!

This is the same syntax you will have used with any JS loader, with the obvious exception of require being replaced by curl.

curl.js with next

The next method allows for chaining of module loading:

	.next(["dep1", "dep2", "dep3"], function (dep1, dep2, dep3) {
		// Execute regardless of domReady status
		function () {
		// do something after the dom is ready
		function (ex) {
		// show an error to the user

This syntax may suit your fancy more than others.

curl.js with Deferred Syntax

If you work with the Dojo Toolkit, or more recently with jQuery, Deferreds are becoming more prevalent and incredibly useful; curl.js provides you the ability to write your loader JavaScript in the same fashion:

	function(depA) { // success callback
	function(depB) { // errback

The deferred format and ability to pass the result of an XHR pool can be very powerful.

Loading Non-AMD JavaScript Files

Sometimes you need to load JavaScript files that aren't in AMD format, like loading MooTools or jQuery from CDN. curl.js makes that easy:

).next(["namespace/MooModule"], function() {
	// We loaded Moo first, then once loaded, loaded a dependency that requires MooTools
	// At this point, both are loaded and we can work with them!

All you need to do add the js! prefix to the dependency string and you're set; your callback will be fire when the basic JavaScript file is loaded. Note that you can mix AMD modules with basic JavaScript files:

).next(["namespace/MooModule"], function() {
	// We loaded Moo first, then once loaded, loaded a dependency that requires MooTools
	// At this point, both are loaded and we can work with them!

Loading CSS Files

Of course one of the strengths of AMD is modularity, so why not load your stylesheets with your scripts?

	function(MyWidget) {
		// Do something with MyWidget
		// The CSS reference isn't in the signature because we don't care about it;
		// we just care that it is now in the page

LINK tags don't provide an onLoad event in all browsers, but curl.js' shim provides a reliable method of detecting stylesheet load. Since stylesheets are a large part of UI-driven, JavaScript-powered widgets, creating modules with stylesheet dependencies is becoming much more abundant.

More curl Plugins

curl is much more than just a basic JS loader. I've already mentioned the JS and CSS plugins above, but curl has a few more. curl features a domReady plugin, as well as a text plugin and an internationalization plugin:

		"i18n!stuff/nls/strings", // Load string content for the user's namespace
		"text!myWidget/resources/template.html", // Loads a file as text,
		"domReady!" // Don't fire the callback until the DOM is ready
	function(nlsStringObject, template) { // Callback
		// Do something now that we have the NLS object, template, and domContentLoaded has fired

These plugins are quick and easy enhancers to existing functionality!

curl is an absolute beast of a JavaScript loader. Beyond simple AMD loading, curl is fit with numerous configuration options, plugins, and multiple syntax structures to all the developer to code the way they want. This blog uses curl.js to asynchronously load JavaScript modules and stylesheets, manage domReady, and more; the best endorsement I can give!

Track.js Error Reporting

Recent Features

  • Responsive and Infinitely Scalable JS Animations

    Back in late 2012 it was not easy to find open source projects using requestAnimationFrame() – this is the hook that allows Javascript code to synchronize with a web browser's native paint loop. Animations using this method can run at 60 fps and deliver fantastic...

  • An Interview with Eric Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

Incredible Demos

  • Sexy Opacity Animation with MooTools or jQuery

    A big part of the sexiness that is Apple software is Apple's use of opacity. Like seemingly every other Apple user interface technique, it needs to be ported to the web (</fanboy>). I've put together an example of a sexy opacity animation technique...

  • Duplicate the jQuery Homepage Tooltips Using Dojo

    The jQuery homepage has a pretty suave tooltip-like effect as seen below: Here's how to accomplish this same effect using Dojo. The XHTML The above HTML was taken directly from the jQuery homepage -- no changes. The CSS The above CSS has been slightly modified to match the CSS rules already...


  1. Yo Dave! I am honored!

    I love this article (for several reason). :)

    One quick suggestion: when you’re looking at your code in the editor, it’s easy to tell what module you’re looking at by the file path. In code snippets, it’s harder to tell so I usually do put a comment at the top of the module declaration. Something like this:

    // module namespace/depC
    define(["namespace/depA", "namespace/depB"], function(depA, depB) {
      // Whole bunch of processing
      // Return what this module defines
      return function() {
        // Or an object, or whatever

    Just another quick note about “curl” vs. “require”:

    The main reason curl doesn’t declare a global `require` by default* is that at the global level, you’re not in a standard environment. The standard `require` — also called the “local require” — exposes functionality that depends on the context of the current module (i.e. what package it’s in or where is it located within the entire folder structure of the app). I’ve seen many n00bs (using other loaders) grab a reference to the global `require`, expect it to behave in a standard way, and spend hours trying to figure out why it doesn’t work.

    Even worse: I’ve seen devs take advantage of proprietary features on another loader’s `require` object and then curse when their code isn’t portable to other loaders.

    curl.js only exposes proprietary features on the global object, not on the local require. In summary: having the global object expose proprietary features and keeping the local require 100% compliant to the standards will help avoid common n00b errors and protect your code from becoming unportable. (The goal of the AMD standard is to allow us all to write portable, reusable code.)

    There are some environments that may need a global “require”. For instance, test harnesses for AMD plugins will likely need to test with several loaders. curl.js has a config option for this.

    curl({ apiName: "require" });

    For reference to readers, here’s how to explicitly grab curl.js’s standard `require` and global, proprietary API object (a.k.a. the “curl” object):

    // my/awesome/modyule:
    define(["require", "curl"], function (require) {
    	// using the standard, local require:
    	var foo = require("foo");
    	return {
    		sillyAbstractionOfFoo: function () { return foo; }
    // my/awesome/modyule
    define(["curl"], function (curl) {
    	return {
    		// using curl's proprietary API to return a promise
    		getAndWaitForAModyule: function (moduleId) {
    			return curl([moduleId]);

    curl.js makes it pretty explicit when you’re doing something non-standard and I like that.


    — John

  2. Alex

    This is by far my favorite article of this year so far.
    Useful and in time. Never though I’d say that to another man…

  3. Manu

    Hi David, fyi the development of i18n plugin for curl seems in progress. Actually the plugin does not work.

  4. Paul

    How does one use the module defined using define ? I just can’t find that information anywhere.
    e.g. a moduleC is defined like: In a file: moduleC.js

    // stuff
    return function(){

    I guess this must be defined in the file moduleC.js
    How would I use moduleC in another file or module? what happens if I define more than one module in a file and I want to give each module a different name ?
    Appreciate any help.

  5. I’d love to learn the basics of AMD/Curl and use with non AMD libraries.

  6. Moritz

    To keep this article up to date:
    The repro from unscriptable moved to cujoJS: https://github.com/cujojs/curl :)

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Making Data Management Easy with Transpose

    One problem with data collection is that we all want capture to be quick and we all want data to be organized -- but there's seldom a utility that allows both. Evernote allows note-taking but it can lose its structure...and no one wants...

  • Serve a Directory via Python

    Sometimes I'm working with a test HTML file and some JavaScript but need to work off of a served space.  In that case, I sometimes need to swap out folders within MAMP Stack which leads to a maintenance nightmare.  Bleh. I recently found out that you can...

  • OSCON Portland:  Conference  Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...

  • Follow Redirects with cURL

    I love playing around with cURL. There's something about loading websites via command line that makes me feel like some type of smug hacker, just like tweeting from command line does. I recently cURL'd the Google homepage and saw the following: I found it weird that Google...

  • Developers Have WordPress, Amateurs Have Squarespace, Professional Designers Have the NEW Webydo!

    Web design platforms have traditionally come in one of two varieties. There are the solutions like WordPress and Drupal that are incredibly powerful, but an understanding of web development and coding is required to be able to use those platforms effectively. On the other side of the...