Build & publish your own private npm package for free

mocasalter profile image Monica Salter ・5 min read

Note: This is a real example, not a tutorial on how to print "hello world" with an npm package. So buckle up buckaroo. 🤠

Do you want to easily share code between projects? Do you want to keep that code private but avoid the monthly fee for privately publishing to npmjs dot com? Then this is the tutorial for you.

We'll be building an npm package, adding JavaScript and CSS, uploading to a private git repo on Bitbucket and making a simple project to test the package with.

Final product: https://github.com/mocasalter/npm-package-demo

When we're done your package will accessibly✨ hide outline focus styles for mouse users but show them for keyboard users*. Like this:

Button Outline Demo

Pre reqs

I assume that you know how to use the command line, have the npm CLI and NodeJs installed and know how to use them, and have git installed and have a decent grasp of how it works.

1. Start a new package

In your CLI, run these commands to make a new folder, navigate into that folder and initialize a new npm package.

mkdir smart-focus-ring
cd smart-focus-ring
npm init -y

Make a new folder called lib and a new file called index.js. Index is the default entry point file for an npm package, and lib will hold the custom files.

mkdir lib
touch index.js

Then open the smart-focus-ring folder in a text editor. If you have Visual Studio Code installed you can run the command $ code ..

Open package.json and add this files array. That will ensure the lib directory is downloaded whenever the package is installed.

  "otherStuff": "",
  "files": [

2. Add JavaScript

Create a new file in lib called smart-focus-ring.js and paste in this code.

function handleFirstTab(e) {
  if (e.keyCode === 9) {
    // the "I am a keyboard user" key
    window.removeEventListener('keydown', handleFirstTab);

function smartFocusRing() {
  window.addEventListener('keydown', handleFirstTab);

module.exports = smartFocusRing;

It adds a class user-is-tabbing to the body when the user hits the tab key.

That last line module.exports exposes the smartFocusRing function as a module so we can use it in index.js.

3. Export the function

Now import smartFocusRing into index.js.

const smartFocusRing = require('./lib/smart-focus-ring.js');
module.exports = smartFocusRing;

module.export-ing from the package's entry point (index.js) makes smartFocusRing available to any project that has installed the smart-focus-ring package.

4. Add CSS

Now add CSS to make the style changes based on whether the user-is-tabbing class is present on the body.

In the lib folder, add a new file called styles.css and paste in this code.

/* Hide the focus ring until we know it's needed */
textarea:focus {
    outline: none;

.user-is-tabbing button:focus,
.user-is-tabbing input:focus,
.user-is-tabbing select:focus,
.user-is-tabbing textarea:focus {
    outline: 2px solid #7aacfe !important; /* for non-webkit browsers */
    outline: 5px auto -webkit-focus-ring-color !important;

Your directory should look like this.

│   │   smart-focus-ring.js
│   │   styles.css
│   index.js
│   package.json

5. Publish to Bitbucket

At this point you could publish to npm if you wanted to but there's a monthly fee for private packages so we'll use Bitbucket instead.

Run these commands to create a new repo inside the smart-focus-ring folder.

cd /path-to/smart-focus-ring
git init
git add .
git commit -m "Initial commit"

Then create an account on bitbucket.org and a new, private git repo on Bitbucket.

Bitbucket's Create New Repo Screen

Run these commands to upload your local repo to Bitbucket.

git remote add origin git@bitbucket.org:YOURUSERNAME/smart-focus-ring.git
git push -u origin master

And you'll see a warning that looks something like this.

Warning, permission denied

This is where things get interesting.

SSH keys

That warning comes up because you haven't given your computer permission to use your private Bitbucket repo yet. To do so, follow Atlassian's walk through on setting up SSH keys for Bitbucket for your operating system.

...Or if your privacy isn't that important, you can go to your Bitbucket repo, select Settings and uncheck the box for "This is a private repository."

Then run this command again and that warning should be gone.

git push -u origin master

6. Make a test project

Create a new project folder, initialize npm and make some files.

mkdir tester-project
cd tester-project 
npm init -y
touch index.js
touch index.html

Add some html to index.html.

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tester Project</title>
        body{font-family:"Open Sans",sans-serif;}
        button{border:none;border-radius:0;padding:0.65em 1.2em;cursor:pointer;color:#fff;background:#8d6e91;}
    <h1>Tester Project</h1>
    <button type="button">button</button>
    <script src="index.js"></script>

And install a bundler so we'll have ES6 module support.

npm install -g parcel-bundler

Run the bundler and go to the url it gives you. Mine was localhost:1234.

parcel index.html

Now try clicking the button. See how it has a focus ring? Once the package is installed, that focus ring will only show if you've indicated keyboard navigation by hitting the tab key.

Button with Outline

7. Import the module

Install the package using your Bitbucket user name.

npm i git+ssh://git@bitbucket.org:YOUR-USER-NAME/smart-focus-ring.git

Import the smart-focus-ring assets into the index.js file and call the smartFocusRing function.

import smartFocusRing from 'smart-focus-ring';
import './node_modules/smart-focus-ring/lib/styles.css';

Then run Parcel and go to the url it provides.

parcel index.html

Now if you click the button the focus ring will be gone, but it will appear when you hit the tab key. All thanks to your npm package! Congratulations! 🥳

Button Outline Demo

*As seen in Removing that ugly :focus ring (and keeping it too).

Posted on Apr 26 by:

mocasalter profile

Monica Salter


JavaScript, React, many flavors of CSS


markdown guide