A few weeks ago, the jQuery plugins website, which developers used to find and download plugins for the popular client-side library, was switched to read-only mode. Developers are now encouraged to use npm to publish and search for jQuery plugins.

This demonstrates how central npm has become to the JavaScript community. It originally arose as the package manager for node.js, but quickly proved itself versatile for any kind of JavaScript code, and developers started using it to host client-side libraries as well.

In this tutorial, we will show you how you can use npm to develop a small web application. We will start from scratch - we will set up a package.json, install a bunch of libraries and make things work nicely together.

How to install and use client-side libraries with npm

There are two ways to do this. We are going to use the simpler one, which is a great fit for small websites and apps:

We will install the libraries that we need with npm. You can see a bunch of jQuery plugins here. To install one of them, run the command npm install <package-name> --save npm creates the node_modules folder and places the libraries there. In our HTML file, we include the scripts and CSS files directly from the node_modules folder with <script> and <link> tags. When the time comes to put your web site/app online, just upload the node_modules folder together with the other files.

This is similar to how Bower works, but has the benefit that we are only using npm without installing additional package managers.

Setting things up

We are ready to start coding! However, there are a few things that you have to do first:

Make sure that you have node.js installed. If you don't, download an installer for your OS and run it. This will also set up npm for you. Create a new empty folder for your new website. As an example, we will use project-folder throughout this tutorial. Open a terminal (or a command prompt if you are on Windows) and navigate to the project folder (cd is your friend!). Type npm init . This will create an empty package.json file. Press enter to use the defaults, if you don't know what info to supply.

Great! Now you've got an empty folder with a valid package.json inside it. package.json is a special file which is used by npm to write down the libraries you've installed so far, and details about your project.

Let's install some libraries

We are going to make a simple web app that will visualize addresses using Google Maps, and will let people save addresses in their browser's localStorage. For this purpose, we will need a bunch of libraries which are available on npm:

npm install bootswatch gmaps jquery moment --save

This will download and write to node_modules Bootswatch (Bootstrap with pretty themes applied), gmaps (an easy way for working with Google Maps), jQuery and moment.js (library for working with date and time in JavaScript). The --save flag will write them to package.json in addition to downloading them.

All that is left is to include these libraries in your HTML.

Tutorialzine NPM-Driven Website

The HTML

We have a basic HTML5 document with a few Bootstrap components. Notice how we've included the bootswatch stylesheet and the libraries by directly specifying their path inside the node_modules folder.

index.html

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Creating an Npm-driven Website</title> <link href="node_modules/bootswatch/flatly/bootstrap.min.css" type="text/css" rel="stylesheet" /> <link href="assets/css/styles.css" type="text/css" rel="stylesheet" /> </head> <body> <div class="container"> <h1>Your Google Maps Locations</h1> <form id="geocoding_form" class="form-horizontal"> <div class="form-group"> <div class="col-xs-12 col-md-6 col-md-offset-3"> <div class="input-group"> <input type="text" class="form-control" id="address" placeholder="Enter your location..."> <span class="input-group-btn"> <span class="glyphicon glyphicon-search" aria-hidden="true"></span> </span> </div> </div> </div> </form> <div class="map-overlay"> <p>Loading...</p> <div id="map"></div> </div> <div class="col-xs-12 col-md-6 col-md-offset-3 save-container"> <h4 id="save-location"></h4> <span class="glyphicon glyphicon-star-empty" aria-hidden="true"></span> </div> <div class="list-group col-xs-12 col-md-6 col-md-offset-3"> <span class="list-group-item active">Saved Locations</span> </div> </div> <!-- The jQuery library --> <script src="node_modules/jquery/dist/jquery.min.js"></script> <!-- The Moment.js library --> <script src="node_modules/moment/moment.js"></script> <!-- Including the Google Maps API and the GMaps library --> <script src="http://maps.google.com/maps/api/js?sensor=true"></script> <script src="node_modules/gmaps/gmaps.js"></script> <!-- Including our own JavaScript file --> <script src="assets/js/script.js"></script> </body> </html>

I have chosen the modern-looking Flatly theme from Bootswatch, which we installed a moment ago. In the HTML you can also see some of Bootstrap's grid classes, along with a list group for presenting the favorite locations.

The JavaScript

Our JavaScript file will handle saving to and reading from localStorage, creating Google Maps using the Gmaps library and converting from addresses to geographic coordinates. You can see the entire file below.

assets/js/script.js

$(function(){ var saveContainer = $('.save-container'), favoriteIcon = saveContainer.find('.glyphicon'), favoriteLocationsListGroup = $('.list-group'); var hasFavoriteLocations = false; // Initialize a google maps using the gmaps library. var map = new GMaps({ el: '#map', lat: '0', lng: '0', zoom: 1 }); // Initialize the favorite locations array which is kept in localStorage if(!localStorage.hasOwnProperty('favorite-locations')) { localStorage.setItem('favorite-locations', JSON.stringify([])); } hasFavoriteLocations = JSON.parse(localStorage.getItem('favorite-locations')).length ? true : false; // Form submit and Search icon handlers $('.glyphicon-search').click(showLocationByAddress); $('#geocoding_form').submit(showLocationByAddress); // Click handler on any of the favorite locations $(document).on('click','a.list-group-item', showLocationByCoordinates); // Click handler on the favorite(star) icon to become saved or removed $(document).on('click', '.glyphicon-star', removeFavoriteLocation); $(document).on('click', '.glyphicon-star-empty', saveFavoriteLocation); // If there are any favorite locations, append them to the favorite location list if(hasFavoriteLocations) { var array = JSON.parse(localStorage.getItem('favorite-locations')); favoriteLocationsListGroup.empty(); favoriteLocationsListGroup.append('<span class="list-group-item active">Saved Locations</span>'); array.forEach(function(item){ favoriteLocationsListGroup.append('<a class="list-group-item" data-lat="'+item.lat+'" data-lng="'+item.lng+'" data-createdAt="'+item.createdAt+'">'+item.address+'<span class="createdAt">'+moment(item.createdAt).fromNow()+'</span><span class="glyphicon glyphicon-menu-right"></span></a>'); }); favoriteLocationsListGroup.show(); } // This function presents the address which was entered in the text field in the map function showLocationByAddress(e) { e.preventDefault(); // Getting the coordinates of the entered address GMaps.geocode({ address: $('#address').val().trim(), callback: function(results, status) { if (status !== 'OK') return; var latlng = results[0].geometry.location, fullAddress = results[0].formatted_address, isLocationFavorite = false, locationsArray = JSON.parse(localStorage.getItem('favorite-locations')), saveLocation = $('#save-location'); var map = new GMaps({ el: '#map', lat: latlng.lat(), lng: latlng.lng() }); // Adding a marker on the wanted location map.addMarker({ lat: latlng.lat(), lng: latlng.lng() }); // Checking if this address exists in the favorites array if(locationsArray.length) { locationsArray.forEach(function (item) { if (item.lat == latlng.lat() && item.lng == latlng.lng()) { isLocationFavorite = true; } }); } // Adding the address to the html and setting data attributes with the coordinates saveLocation.text(fullAddress).attr({'data-lat': latlng.lat(), 'data-lng': latlng.lng()}); // Removing the active class from all favorite locations favoriteLocationsListGroup.find('a.list-group-item').removeClass('active-location'); // Changing the icon to become non-favorite if(!isLocationFavorite) { favoriteIcon.removeClass('glyphicon-star').addClass('glyphicon-star-empty'); } else { // Adding the active class and add the favorite icon on the given favorite location favoriteIcon.removeClass('glyphicon-star-empty').addClass('glyphicon-star'); // Find the entry in the favorite locations list that corresponds // to the current location, and mark it as active. favoriteLocationsListGroup.find('a.list-group-item[data-lat="'+latlng.lat()+'"][data-lng="'+latlng.lng()+'"]').addClass('active-location'); } // Show the html of the given location saveContainer.show(); } }); } // This functions is called when a favorite location is clicked. // It reads the coordinates and shows them in a map function showLocationByCoordinates(e) { e.preventDefault(); var elem = $(this), location = elem.data(); // Getting the address from the location's coordinates GMaps.geocode({ location: {lat: location.lat, lng: location.lng}, callback: function(results, status) { if (status !== 'OK') return; var fullAddress = results[0].formatted_address, saveLocation = $('#save-location'); var map = new GMaps({ el: '#map', lat: location.lat, lng: location.lng }); map.addMarker({ lat: location.lat, lng: location.lng }); // Adding the address to the html and setting // data attributes with the location's coordinates saveLocation.text(fullAddress); saveLocation.attr({ 'data-lat': location.lat, 'data-lng': location.lng }); // Adding colored background to the active favorite location and // removing the old active location favoriteLocationsListGroup.find('a.list-group-item').removeClass('active-location'); favoriteLocationsListGroup.find('a.list-group-item[data-lat="'+location.lat+'"][data-lng="'+location.lng+'"]').addClass('active-location'); // Add the favorite icon on the given location favoriteIcon.removeClass('glyphicon-star-empty').addClass('glyphicon-star'); // Show the html of the given location saveContainer.show(); // Clear the search field $('#address').val(''); } }); } // This function saves a location to favorites and adds it to localStorage function saveFavoriteLocation(e){ e.preventDefault(); var saveLocation = $('#save-location'), locationAddress = saveLocation.text(), isLocationFavorite = false, locationsArray = JSON.parse(localStorage.getItem('favorite-locations')); var location = { lat: saveLocation.attr('data-lat'), lng: saveLocation.attr('data-lng'), createdAt: moment().format() }; // Checking if this location is in the favorites array if(locationsArray.length) { locationsArray.forEach(function (item) { if (item.lat == location.lat && item.lng == location.lng) { isLocationFavorite = true; } }); } // If the given location is not in favorites, // add it to the HTML and to localStorage's array if(!isLocationFavorite) { favoriteLocationsListGroup.append( '<a class="list-group-item active-location" data-lat="'+location.lat+'" data-lng="'+location.lng+'" data-createdAt="'+location.createdAt+'">'+ locationAddress+'<span class="createdAt">'+moment(location.createdAt).fromNow()+'</span>' + '<span class="glyphicon glyphicon-menu-right"></span>' + '</span></a>'); favoriteLocationsListGroup.show(); // Adding the given location to the localStorage's array locationsArray.push({ address: locationAddress, lat: location.lat, lng: location.lng, createdAt: moment().format() }); localStorage.setItem('favorite-locations', JSON.stringify(locationsArray)); // Make the star icon full, to signify that this location is now favorite favoriteIcon.removeClass('glyphicon-star-empty').addClass('glyphicon-star'); // Now we have at least one favorite location hasFavoriteLocations = true; } } // This function removes a favorite location from the favorites list // and removes it from localStorage function removeFavoriteLocation(e){ e.preventDefault(); var saveLocation = $('#save-location'), isLocationDeleted = false, locationsArray = JSON.parse(localStorage.getItem('favorite-locations')); var location = { lat: saveLocation.attr('data-lat'), lng: saveLocation.attr('data-lng') }; // Removing the given location from the localStorage's Array if(locationsArray.length) { locationsArray.forEach(function (item, index) { if (item.lat == location.lat && item.lng == location.lng) { locationsArray.splice(index,1); isLocationDeleted = true; } }); } if(isLocationDeleted) { // Remove the given location from the favorites list favoriteLocationsListGroup.find('a.list-group-item[data-lat="'+location.lat+'"][data-lng="'+location.lng+'"]').remove(); localStorage.setItem('favorite-locations', JSON.stringify(locationsArray)); // Removing the favorite icon from the html favoriteIcon.removeClass('glyphicon-star').addClass('glyphicon-star-empty'); if(!locationsArray.length) { // There are no more favorite locations hasFavoriteLocations = false; favoriteLocationsListGroup.hide(); } else { hasFavoriteLocations = true; } } } });

The CSS

We mostly rely on Bootstrap with the Flatly theme to do the styling for us. However I did write a few additional CSS rules, which you can see in assets/css/styles.css in the downloadable zip with the source code.

To wrap it up

This concludes our tutorial! Npm has a huge number of JavaScript libraries, a lot of which are usable in the browser directly (for the rest we have Browserify, but this is a topic for another article). Do you think you will use npm in your client side development? Share your thoughts in our comment section.