Dynamically generate TypeScript Playground links

🌱May 9, 2024.
Last tended May 11, 2024.
budding 🌿
2 minutes read ⏱

Recently I was presenting a talk at SeattleJS on Branded Types using my blog post as the visual content. I wanted to do some live coding, so I decided to add TypeScript playground links to my code snippets.

Fun fact: it’s possible to dynamically generate TypeScript playground links!

The process is fairly simple:

  1. Use lz-string to base64 encode and zip the source code and URI encode the result (use LZString.compressToEncodedURIComponent(), an easy one-liner)
  2. Append the encoded string to a playground URL
  3. Put the link on the code block

I love this solution because it generates an updated link every time I build my site. I’m guaranteed to have up-to-date playground links and don’t have to do anything after the initial setup. Awesome!

I followed the TS playground docs to write the code that generates TS playground links. I updated my code highlighter function to generate the TS playground links and add them to a data-tsplay attribute on the containing <pre> tag for each TS code block. Check it out:

import LZString from 'lz-string';
import { codeToHtml } from 'shiki';
import { transformerTwoslash } from '@shikijs/twoslash';
import { lex, parse } from 'fenceparser';
// ---cut---
/** @type Exclude<import('mdsvex').MdsvexOptions['highlight'],false | undefined>['highlighter'] */
async function ShikiTwoslashHighlighter(code, lang, meta) {
	let tsPlaygroundLink = undefined;
		if (lang === 'ts') {
			// Generate a TS Playground link
			tsPlaygroundLink = `https://typescriptlang.org/play/#code/${
				LZString.compressToEncodedURIComponent(code)
			}`;
		}
	const html = await codeToHtml(code, {
		transformers: [
			transformerTwoslash({ explicitTrigger: true }),
			// This custom transformer adds the data-tsplay attribute
			{
				name: 'TSPlaygroundLinkInserter',
				pre(el) {
					if (tsPlaygroundLink !== undefined)
						el.properties['data-tsplay'] = tsPlaygroundLink;
				}
			},
			// other transformers...
		],
		// other config options...
	});
	

With that I have playground links available in my output HTML. The last step I have to Make example code interactive on my blog is to Add a demo link to TypeScript code blocks.

I’m so glad I ended up doing this. Being able to quickly pop open a live editor to make changes and show runtime values made my Seattle JS talk much better!