Build 02/30: TypeScript Mini Apps in Eleventy

Posted Tuesday, October 14, 2025 by Sri. Tagged JOURNAL

I added "mini web app" functionality for my Eleventy template pages. I can create individual Typescript source files inside a project directory that works with live-reloading.

Today's Sharepiece

Build 02 - A prototype integration of TypeScript into Eleventy templates for live-editing mini web apps.screenshot of Eleventy template and TypeScript source resulting in rendered pagescreenshot of Eleventy template and TypeScript source resulting in rendered page (full size image)

Today's daily build challenge sharepiece is workflow for incorporating TypeScript modules into an Eleventy template. It's built on top of the code that I assembled yesterday. I've included a link to the build system below for the curious.

The Building Process

My current mini-goal is to make an "Activity Bingo Board"See A Bingo Board for Task Focusing tracking sheet that will help keep me focused on useful tasks for 30 Day Building Challenge. Rather than draw this in Affinity Designer, I'm writing software to do itWhy do this the hard way? Software will automate form creation and is a stepping stone toward making productivity apps. I can also refine my code libraries for future freelance projects.. Not only that, I want to write that software so it runs inside of my Eleventy PKMS, turning it into a compute -capable notebook.

Theory of Operation

I created an HTML template which looks like this:

Figure 1. This template entry expeditions/build-2025/day02.html is in my Eleventy src directoryEleventy html template, showing markdown and html script tagsEleventy html template, showing markdown and html script tags (full size image)
  • The MD shortcode is something I made just to pipe the content through the markdown processor, since HTML templates don't do this by default.
  • line 15 defines an SVG container witha few inline styles.
  • line 17 loads an ES module from the designated path.

The reason this is an HTML template is that if you use a regular Markdown template with the .md extension, blank lines turn into malformed paragraph tags that messes up the script.

The javascript sourcefile is transpiled from this TypeScript source:

Figure 2. This TypeScript source is located in app-source/day02.ts in a repo outside of the PKMS, symlinked as src-codelab at the same level as src so it's visible in my Visual Studio project.TypeScripts source file to be run by the Eleventy html templateTypeScripts source file to be run by the Eleventy html template (full size image)
  • line 2 shows an import from my URSYS realtime web app framework, showing proof of concept for importing external modules
  • line 12 shows that a formatted console log will be emitted to the console

The resulting page looks like this:

Figure 3. The web page Javascript console (right) shows the emitted fancy console output.

This version of the page also shows an animated red square in the SVG element.
Page on dsriseah.com showing the script in day02.ts running inside of day02.html templatePage on dsriseah.com showing the script in day02.ts running inside of day02.html template (full size image)
  • Note that source mapping is enabled in the Javascript console. It's off by one because this version of day02.ts is different than Figure 2.

Hacking the Eleventy Directory Structure

The Trouble with Configuration Files

Notably, tooling configuration files like tsconfig.json, .eslintrc.js are searched for by looking in the root directory, which is Eleventy. The same happens with NodeJS module resolution with node_modules. Visual Studio Code's Intellisense and add-ons ALSO looks for this for live linting in the editor, and is easily confused.

Since my root folder is Eleventy using CommonJS and the src-codelab folder is TypeScript-ESM, VSCode gets confused without very tricky configuration.

The TypeScript transpiler system is located in the root Eleventy folder in src-codelab. This is a symlink to another directory, as Node-based configuration files don't play nice in nested projects. By putting src-codelab in another directory, this helps isolate it from ambiguity (see margin note).

The transpiler system itself is based on yesterday's Web App Template sharepiece, but modified specifically for this purpose. Instead of building a single Javascript bundle, it builds multiple ones. By simply creating a TypeScript file with a .ts extension in codelab-2025/app-source, the build system will general bundled js with the same name.

Symlink Directory Structure

Repos/
  sri-garten/
    src/
      expedition/
        build-2025/
*         js/
          day02.html
*   src-codelab/
> codelab-2025/
    _public/
>     js/
        day02.js
    app-source/
      day02.ts

* is a symlink, > is a symlink source

Here's a terminal run showing how the webapp is building the js bundle. It's in the codelab-2025 project folder outside of my eleventy folder, but as mentioned above it's symlinked to src-codelab next to the src directory (see margin note).

  • the contents of the _public/js directory are symlinked to my Eleventy install's src/expedition/build-2025/js folder, so it's available
  • the js/day02.js bundle is linked in Figure 1, line 17

Why go through all this symlinking trouble, you ask?

  • with the src-codelab symlink in my Eleventy VSCODE project, I can edit the .ts files directly and they will live-reload
  • with the js symlink hooked into the expedition/build-2025/js directory, I can use relatively short relative links in the <script> tags of the expedition templates (e.g. day02.html)
  • when I deploy my site with rsync, all the bundle files go along with it.

To get the transpile+live-reload behavior, I just run the build tool in a separate terminal as seen in the Asciinema terminal cast above.

The Current Eleventy Workflow

I use a custom command line utility called garten that creates entries according to my knowledge manage system categories. It's as simple as doing this:


  garten new expedition build-2025/day99.html

This pre-populates the new day99.html file. Or I could just copy/paste another file and use that as a starting point.

To add code to this, I add the script lines:


  <script type="module" defer src="../js/day99.js"></script>

Since day99.js doesn't exist, I need to go to src-codelab/app-source/ and create day99.ts. This is a TypeScript file inside a custom esbuild-based framework (my URSYS library).

Next, I want to run the build by opening another terminal window and running npm run dev. This transpiles any .ts files it founds in app-source to a standalone .js bundle. I leave this running so it will live-recompile the bundle file.

For live editing of the Eleventy day99.html template, I run my garten command by itself, which spawns the Eleventy DevServer. It's configured to do full-page reloads instead of diffs, otherwise the Javascript context will get confused every time the JS bundle changes.

And that's it!

COMPANION CODEBERG REPO

You can grab the build system example that transpiles TypeScript to Javascript.

This repo is based on my always-in-development URSYS library, and probably isn't of much use to other people. It's provided for curious minds. Note also that this is ONLY the transpiler I discussed above, and is not a full Eleventy setup!

All Building Challenge Posts

This challenge starts October 11th and ends when 30 artifacts have been posted. Weekends are exempt from production.

URSYS Web App Template

Embedded TypeScript Apps in Eleventy

BUILD CHALLENGE COMMENTARY

Today's build challenge sharepiece gets me closer to drawing graphical representations of data structures from within my personal knowledge management system. This gives me functionality that's somewhat like a very primitive Jupyter Notebook or Observable Notebook. Implementing my own version is more suitable for developing my own custom software solutions for making printable forms, and it's a good way to learn new techniques that could lead to future freelance projects.

Today's building challenge took a LOT of time, including several hours to write this blog post and make a janky title picture. But this is part of the training! Rawr!

Additional accomplishments of the day:

  • I forgot how to do a Github tagged release. I needed to make a quick fix to the URSYS library and stumbled around for a while. I added Procedure: Creating Releases to the URSYS WIKI
  • I have never used asciinema before, so that was cool!
  • I had to dig into Eleventy configuration to figure out why my .md templates were ruining my <script> tags when there were blank lines. AI search identified the problem with Eleventy (yay) but messed up the solution (typical).

Tomorrow I hope to actually draw something from a data structure that could be used for a dynamically-generated Activity Bingo Board. But I may take a break from code and work on business cards as I want to share them with the friends I made at the Farmer's Market before the season is over.


I yammer about daily productivity challenges on the DSRI Discord Community Server every day. If you are a human-centered architect-builder that likes to chat, you might like the vibe.

Or chat with me here on Mastodon / Bluesky