There is nothing that prevents you from using native JavaScript Modules (ESM or ES Modules) on CodePen. But there is stuff to know!

Importing Pens

Pens have the ability to add External Resources in the JavaScript area of the Pen’s Settings. What that does is inject a <script> into the HTML for that resource. That’s not ES Modules, it’s just often used in a similar fashion. It’s also important in that it uses the same CodePen-specific concept: that any given Pen can output its JavaScript code by appending a .js file extension to the URL:

https://codepen.io/chriscoyier/pen/bGBwmLa.js

That very Pen has JavaScript in it like this:

export const NAME = "Chris Coyier";

export function AddThings(a, b) {
  return a + b;
}

Now in another Pen, I could write a line of JavaScript that imports bits of that like this:

import { MyName } from "https://codepen.io/chriscoyier/pen/bGBwmLa.js";

console.log(MyName); // Chris Coyier

That’s ES Modules!

The Pen Editor

Above I just described how ES Modules work, but there are couple of things to note. In order for ES modules to work, there needs to be a type attribute on the script. Like this:

<script src="..." type="module"></script>

Because we control the HTML template, that means we need to put that type attribute in place. We have fancy code that does it’s best to guess if it should be there or not. If you think it’s not doing it correctly, let us know in support.

The Pen Editor also has a way to search for external resources that work with ES Modules. See the section on npm for that.

The Project Editor

The Project Editor does not automatically template your HTML. That means if you’re going to use ES Modules in a JavaScript file, you need to remember to add the type attribute when linking it up:

<script src="./main.js" type="module"></script>

You can also use ES Modules that link out to fully-qualified URLs. Here’s an example Project that does both, importing an npm module from Skypack and importing a local variable:

import { type } from "https://cdn.skypack.dev/@camwiegert/typical";
import { NAME } from "./util/constants.js";

Some folks like using the file extension .mjs to indicate a file is using ES Modules. That doesn’t do anything special (CodePen or elsewhere), but CodePen Projects does support that file extension in case you prefer using it.

Using Packages from npm

In a lot of tutorial on the web, you might see lines like this:

import React from 'react';
import ReactDOM from 'react-dom';

While those look at lot like native ES Modules, they are not. Lines like that assume you have a package.json and are doing an npm install such that you have an node_modules folder to import from. That’s true on a lot of projects, especially that you develop locally, but not really on CodePen.

Fortunately, Skypack makes this easy to fix. You can import React (any pretty much any other npm package) from Skypack like this:

import React from "https://cdn.skypack.dev/react@17.0.1";

CodePen will help you fix imports like that automatically. You’ll see an icon like this come up, which you click to open and are offered a button to click to fix the issue.

Try it Yourself!

The JS area in Settings also lets you search for npm packages. Clicking the package automatically inserts an import into your JavaScript code:

Important Notes about Skypack/npm Imports

Versions

Skypack allows you to import a specific version of a package by appending @ and the version number.

import lodash from "https://cdn.skypack.dev/lodash@4.17.20";

If you use our Add Package feature, we’ll automatically put the latest version number in the import statement so that nothing will change under your feet. You are, of course, free to use whatever you like by editing the URL in your JavaScript code, or leave off the version number to always use the latest.

Every package is different

It’s usually helpful to console.log() what you get back so you can see what’s in there, like:

import x from "https://cdn.skypack.dev/package-name";
console.log(x);

If that’s null, you might try:

import * as x from "https://cdn.skypack.dev/package-name";
console.log(x);

It depends on if there is a default export in the package. We try to smartly do the right syntax if you add such as the difference here in React and Preact:

React and JSX

If you want to write JSX in your Pens, you have to flip on Babel as the JavaScript preprocessor. That will turn the otherwise-invalid angle brackets in JavaScript into React-style createElement functions (as well as polyfill a bit of future JavaScript stuff).

The trick is that the JSX transformations end up like React.createElement(), so you have to make sure React is defined, meaning you have to import React named like:

import React from 'https://cdn.skypack.dev/react;

For JSX in Projects, you’ve got to flip on the bundler in your Project Settings:

Try an example.

Usage in the Vue Editor

The Vue SFC Editor (Single File Component) not only supports the usage of ES Modules within the JavaScript <script> area, CodePen processes your Vue code into ES Modules friendly code. That means any Vue SFC Pen can be imported into another.

For example, here’s a Vue SFC Pen that imports a confetti package. Then here’s another Vue SFC Pen that imports that button and uses it.

Browser Support of ES Modules

All modern browsers support basic usage of ES Modules. Notably, IE 11 does not. But neither does the CodePen Editor so it’s not great for testing IE 11 anyway.

More

Here’s a video podcast on all this:

Here are some posts about Skypack and CodePen’s package support:

We’ve also talked about all this fun stuff on other podcasts:

Was this article helpful?
YesNo