Embedding Code Into Medium

How to embed code on Medium, and analysis of the options available to you.

Cody Towstik
10 min readMay 28, 2020

TL;DR

Medium doesn’t have native support for syntax hilighting for code snippets. Fortunately, you can embed
other services that support it into Medium articles. Which UI is best?

If you’d like to jump ahead to any sections, or need to quickly access them later, here’s the low down:

1. What We’re Looking For
2. Feature Overview
3. Comparison
4. Overview

Foreword 💃

Medium doesn’t have powerful, native support for embedding code, or anything for that matter. Whether it is a Tweet, Instagram video, code, or something else, Medium relies on a third party service called Embedly.

That sucks, right? Except it doesn’t. Medium can focus on what makes Medium awesome, and leave the nitty gritty details of embedding to the experts at Embedly.

Since Medium utilizes Embedly, any provider that Embedly supports is automatically supported by Medium. Embedly supports over 700 different content providers. All Embedly does is embed an iframe HTML element into the Medium post. The content provider only needs to follow some simple guidelines. If you have an idea for a cool embed, it is relatively straightforward to become an Embedly provider.

Here are some popular supported providers you might know.

Code

  • Github Gists
  • Codepen.io
  • JSFiddle

Video

  • YouTube
  • Vimeo
  • Hulu

Image

  • Instagram
  • Dribbble
  • Imgur
  • Flickr

Media

  • Scribd
  • Prezi
  • Storify
  • SlideShare

You can view the full list of supported providers on Embedly’s website, and read the FAQ for embeds on Medium’s official documentation.

Let’s take a look at some options we have to embed code, in context of how you might practically want to do so. In this article, we’ll explore the three options suggested by Medium in their official documentation.

  1. Github Gists
  2. Codepen.io
  3. JSFiddle

What We’re Looking For

Here is what I’m looking for in my ideal embed:

Clean UI for large and small embeds

We don’t want to embed a clunky, ugly code block that distracts, but a sleek and sexy UI that enhanced the viewing experience. For large code blocks, this isn’t an issue. However, often many smaller 4–5 line embeds are used in sequence to explain and demonstrate small bits of code. If there is a lot of UI boilerplate around the embed, this can look clunky.

Code should look good on Mobile and desktop. For example, I prefer my code blocks to be horizontally scrollable if the screen width is too small to fit, instead of automatically wrapping.

Syntax highlighting for common languages

Syntax highlighting is a must for even the most simple of code blocks. Especially since examples to explain a general concept may use a language that not everyone is familiar with. Syntax highlighting improves readability and understandability. All the other ‘-ility’, too.

Easy to update and organize disparate and related code

I go through code examples I provide with a fine tooth comb, and often copy and paste them from a program that is actually being executed. Still, a typo or incidental bug here or there is likely to slip in. Plus, over time my opinions may change (or be completely wrong!) and code will be updated to reflect that.

Keeping all of my code samples well organized and easy to edit is a must.

Nice To Have

Able to embed a subset of lines from a single file

The ability to embed lines 15–20 from a single source will make my life easier. I won’t have to manage 30 different code bits for one article, updating each one if something should change.

It also improves the user experience because it is clear when an extracted example comes from a larger context. Which… improves readability and understandability. All the other ‘-ility’, too.

Customizable color scheme

Sometimes code looks good on ‘my machine’ because of the settings for my IDE. It might be nice to change the color scheme based on if the user is browsing in Dark Mode, too.

These are all preference based wants, and are what we’ll be using to evaluate each method of embedding code.

Feature Overview

Services like `codepen.io` offer a premium version that may offer more powerful features. For our purposes, we only care about free versions. Although some of the premium features are pretty spicy, so we may take a look at those in another article. Here we’ll take a look at the websites for the services themselves, ignoring how they render on Medium.

Github Gists

Style

Light theme with standard highlighting you’d see on Github.

Languages

GitHub gists are powered by Code Mirror which supports over 100 languages.

  • HTML (Haml, Markdown, Slim, Pug)
  • CSS (Less, SCSS, Sass, Stylus, PostCSS)
  • JS (Babel, Typescript, Coffeescript, Livescript)

Organization

Gists are the most basic tool out of the three main ones. There isn’t a native way to organize your gists, they all just show up in one collection, sortable by ‘created’ or ‘updated’ date. Fortunately, there are some external tools like cacher.io that gives you powerful tools to organize and manage your gists.

Thoughts

Since gists are just Git repositories, they come with all the nice features that you’d expect: revision history, tooling through GitHub’s API, and Starring

The UI is clean and easy to use, as expected from a tool that isn’t trying to do a lot. Notice how there is no execution of code, only the raw source is embeddable.

The documentation around Gists is a little scattered. Here is some deprecated documentation that does a good job giving an overview of gists. The current official ‘documentation’ doesn’t say much and is missing a lot of information, but feel free to take a look. Personally, I’d just read through the gists API documentation to learn more.

Codepen.io

Style

Dark theme with pastel colored syntax highlighting, one customizable theme for free users. Has a side-by-side view of code and executed result.

Languages

  • HTML (Haml, Markdown, Slim, Pug)
  • CSS (Less, SCSS, Sass, Stylus, PostCSS)
  • JS (Babel, Typescript, Coffeescript, Livescript)

Organization

  • Full blown ‘Projects’, with a 1 project limit for free users
  • Group different pens and projects into ‘Collections’

Other

  • Templates to start new code pens with. There are community provided templates, and you can create your own.
  • Support for external scripts and linking to other ‘pens’
  • Premium features

Thoughts

The organization features are relatively powerful, and are enough to keep Pens decently organized. There is no versioning system, so you can’t see previous versions of your snippet.

The UX feels a little clunky, and navigating isn’t immediately obvious. With a little exploring, the UI becomes reasonably intuitive, and the official docs are well fleshed out.

JSFiddle

Style

  • Dark theme with slightly higher color contrast than default Codepen.io dark theme.
  • Initially renders with result of code, but user can choose to show JS code.

Languages

  • HTML (Haml)
  • CSS (SCSS, Sass)
  • JS (Babel + JSX, Typescript, Coffeescript, React, Vue, and more)
  • JS Frameworks (Angular, React, Vue, jQuery, and more)

Organization

  • Group different pens and projects into ‘Collections’
  • You can Save/Update fiddles, adding a new version number. Or Fork Fiddle, splitting the existing Fiddle into a new one, starting at version 0.

Other

  • Default boilerplate to start new Fiddles with.
  • Support for external resource like CSS or JS
  • Premium features, including categorizing aka ‘grouping’ Fiddles

Thoughts

The free version doesn’t offer a way to group your Fiddles. But, it’s nice that you can go back to previous versions of your code, although accessing them is a little wonky. You need to manually change the version number in the url to the version number you want.

e.g.

https://jsfiddle.net/codytowstik/bvdznyux/3

https://jsfiddle.net/codytowstik/bvdznyux/4

The UI of the main website isn’t super obvious, so be sure to check out the official JSFiddle Docs.

The UX was a little wonky at times, partially due to the simple non-obvious UI, but also some things didn’t feel like they were working. In order to update the Title and Description of my fiddle, I had to Fork it each time. Also, accessing previous versions is wonky, as mentioned above.

Comparison

Feature Comparison

Let’s briefly analyze the benefits of each provider and when you may want to choose one over the other, not yet considering how the embeds will actually render.

Displaying code

Gists supports all the languages you’ll probably need, and is the most convenient. Gists are connected right to your Git account, and with cacher.io you can powerfully organize your gists. If you only need to show a single file or are okay with embedding each file sequentially, Github gists is your best bet.

JSFiddle and Codepen are nice fallbacks if you really want to show a set of connected HTML, CSS, and Javascript files in one block, accessed through multiple tabs. Between the two, I’d prefer using Codepen, as the organizational features feel more robust. Keep in mind JSFiddle supports a greater set of languages and javascript frameworks, which may be valuable depending on your use case.

Executing code

Medium doesn’t let you execute code that modifies the DOM of the actual Medium site, for security reasons. Since the embeds are just inline <iframe> elements, they get their own separate execution environments.

Github gists doesn’t support executing code, so your only options are JSFiddle or Codepen. Again, I’d prefer Codepen unless I needed to use a specific language or framework only supported by JSFiddle.

Note: Some of the services offer options for customizing the embeds UI. The customization features aren’t available on Medium, since just the URL is used to embed rather than the full embed script.

Embed Comparison

To evaluate each provider, we’ll be using the code used inanother article I’ve written called What is Testing?. The feature wishlist we defined in What We’re Looking For errs toward just displaying code and not executing it. Similarly,

Remember, to embed code just put the link to the resource. Don’t use any provided ‘Embed’ <script> tags.

Large Blocks

Let’s render the full gist for each provider.

Native

// what_is_testing

/**
* Calculates the number of the specified pokemon caught given a map representing the current Pokedex state.
*
* @param {{pokemon: string, count: number}} pokedexState the sample Pokedex state
* @param {string} pokemonToCount the list of Pokemon to include in the total count
* @returns {number} the total specified Pokemon caught
*/
let calculateTotalSpecifiedPokemonCaught = ( pokedexState, ...pokemonToCount ) => {
let totalPokemonCaught = 0

for ( let index = 0; index < pokemonToCount.length; index++ ) {
let currentPokemon = pokemonToCount[ index ]

totalPokemonCaught += pokedexState[ currentPokemon ]
}

return totalPokemonCaught
}

/**
* @param {string} testID a description to match this result to the executed function
* @param expected expected result of the test function
* @param actual actual result of the test function
*/
let checkResults = (testID, expected, actual) => {
if ( expected === actual ) {
console.log(`SUCCESS test '${testID}'`)
}
else {
// using ES6 template literals to format output string with expected data
console.log( `FAILED test '${testID}' -- expected ${expected} but got ${actual}` )
}
}

//// sample test data

let samplePokedexState = {
'Pikachu': 4,
'JigglyPuff': 2,
'MewTwo': 1,
'Bulbasaur': 1
}

let sampleEmptyPokedexState = {}

//// test calls

let result;

// empty data parameters

result = calculateTotalSpecifiedPokemonCaught( sampleEmptyPokedexState )

checkResults( 'empty data', 0, result )

// non-empty Pokedex, looking for Pokemon that exist within the map

result = calculateTotalSpecifiedPokemonCaught( samplePokedexState, 'Pikachu', 'Bulbasaur' )

checkResults( 'existing Pokemon', 5, result )

// non-empty Pokedex, looking for Pokemon that don't exist within the map

result = calculateTotalSpecifiedPokemonCaught( samplePokedexState, 'Vulpix' )

checkResults( 'missing Pokemon', 0, result )

// non-empty Pokedex, looking for some Pokemon that don't exist within the map, and some that do

result = calculateTotalSpecifiedPokemonCaught( samplePokedexState, 'Vulpix', 'Pikachu' )

checkResults( 'existing and missing Pokemon', 4, result )

// SUCCESS test 'empty data'
// SUCCESS test 'existing Pokemon'
// FAILED test 'missing Pokemon' -- expected 0 but got NaN
// FAILED test 'existing and missing Pokemon' -- expected 4 but got NaN

Github Gists

Codepen.io

JSFiddle

Thoughts

  • Native doesn’t really wrap well and you can easily get lost in long bits of code.
  • Gists shows line numbers, makes it easy to reference a line in the article.
  • Gists has a clean simple UI, the lightmode can be a bit jarring when browsing via mobile dark mode.
  • Gists’ simple UI looks like it ‘fits’ in the page and doesn’t immediately scream <iframe>
  • Codepen dark UI is nice, but no line numbers.
  • Codepen and Gists links back to the original source, so you can easily ‘Star’ or otherwise save and modify each snippet. Gists provides a direct link to the raw source. On mobile, it seems you need to long press to navigate to
    the source, as it is an external link.

Small Blocks

For inline code, I don’t usually care about syntax highlighting, so the native Medium code embed support works fine.

Typically when I reference smaller code blocks in my articles, it is in reference to a larger document. It would be nice to be able to provide line numbers that are contextual to where the small sub-section is within the larger document.
None of our current tools offer support for embedding sub-sections of code frome a single larger snippet. It’s all or nothing.

For now, let’s see how each provider renders a small context-free snippet. We’ll think about our options for embedding more context-aware sub-section snippets after. Here are two small snippets, one with vanilla javascript and the other using Typescript.

Native

// sample enum

enum TestIDEnum {
NOTE_OPTION_FAVORITE = 'note-option-favorite',
NOTE_OPTION_REMOVE_CATEGORY = 'note-option-remove-category',
NOTE_OPTION_RESTORE_FROM_TRASH = 'note-option-restore-from-trash',
NOTE_OPTION_TRASH = 'note-option-trash',
}
// simple 'map' using javascript object notation
let samplePokedexState = {
'Pikachu': 4,
'JigglyPuff': 2,
'MewTwo': 1,
'Bulbasaur': 1
}

Github Gists

Codepen.io

JSFiddle

Thoughts

Woe is me. It is truly a crying shame that there isn’t native support for embedding sub-sections of a single file. My thoughts for small blocks generally mirrors my thoughts for larger blocks.

For small blocks, especially if they are consecutive blocks, Github gists gets a double gold star since the UI integrates visually better with the page. If the block is a sub-section of a larger document, the fact that the line numbers start at ‘1’ for every block makes it feel a bit visually awkward. At least it makes it easier to reference code by line number in the article, but it doesn’t feel “right”.

There is something to be said about the simplicity of the native code blocks, though. If you won’t be needing syntax highlighting, then for small snippets I don’t actually mind the UI. It is better than having all of the noisy additonal UX elements of the embeds, and works well on mobile. Just make sure your code doesn’t wrap (don’t forget about mobile!), or it will appear a bit wonky.

Overview

Each different service has a different approach to tackling similar problems. Let me know in the comments below what you use!

--

--