Building a Reusable Music Component in Astro using ABCjs

Creating an Astro Music Component with ABCjs

I recently built a reusable Astro component that integrates ABCjs, allowing me to easily include music notation in any post or page on my Astro website.

With ABCjs, a versatile JavaScript library, you can easily render beautiful sheet music directly in the browser using ABC notation—a specialized, plain-text format for music. Not only can you visually render notation, but you can also integrate audio playback with synthesized audio.

🎵 What You'll Learn

By the end of this tutorial, you'll have a fully functional Astro component that can render sheet music and provide audio playback using ABCjs.

Creating the AbcjsPlayer.astro

To create the reusable Music Component, I started with a new Astro component called AbcjsPlayer.astro. Here’s a detailed breakdown of the key steps involved:

Step 0: Define the Component Props

To create the Astro component with ABCjs, you’ll first need to install the required dependencies:

🔮 ABCjs library

Make sure to install the ABCjs library using npm install abcjs before proceeding. You won’t be able to render music or use audio features without it!
npm install abcjs

Then, in your Astro component file (AbcjsPlayer.astro), import the necessary modules and define the component props:

// AbcjsPlayer.astro
import { renderAbc, synth } from 'abcjs';
import 'abcjs/abcjs-audio.css'; // Import the required ABCjs audio CSS
 
const { notation, showControls = true, responsive = true } = Astro.props;

Step 1: Define the Component Props

The first step to creating this component is to define the properties it will take. These include the notation, an optional showControls flag for playback buttons, and responsive behavior.

// AbcjsPlayer.astro
interface Props {
  notation: string;
  showControls?: boolean;
  responsive?: boolean;
}

const { 
  notation,
  showControls = true,
  responsive = true
} = Astro.props;

Step 2: Build the HTML Structure for Interactive Music Rendering

For rendering music seamlessly, we need two separate elements inside the abcjs-container:

  1. #paper to render the sheet music notation.
  2. #audio to handle music controls like play and pause.

This structure keeps the visual and interactive elements separate, making them easier to manage and style:

<div class="abcjs-container" data-notation={notation}>
  <div id="paper"></div>
  {showControls && <div id="audio"></div>}
</div>

The data-notation attribute contains the ABC notation format that you want to render. Both paper and audio will be dynamically updated later by the script.

Step 3: Adding ABCjs Rendering Logic

Next, I imported ABCjs and ensured that it runs only on the client-side (window !== ‘undefined’). I used ABCjs’s API to render sheet music and synthesize audio playback.

<script>
  if (typeof window !== 'undefined') {
    import('abcjs').then((abcjs) => {
      function initializeAbcjs() {
        const containers = document.querySelectorAll('.abcjs-container');
        
        containers.forEach((container, index) => {
          const notation = container.getAttribute('data-notation');
          if (!notation) return;

          const paperId = `paper-${index}`;
          const audioId = `audio-${index}`;
          
          const paperDiv = container.querySelector('#paper');
          const audioDiv = container.querySelector('#audio');
          
          if (paperDiv) paperDiv.id = paperId;
          if (audioDiv) audioDiv.id = audioId;

          // Rendering Music Notation using ABCjs
          const visualObj = abcjs.default.renderAbc(paperId, notation, {
            responsive: "resize",
            add_classes: true,
            paddingleft: 0,
            paddingright: 0,
            paddingbottom: 10,
            padddingtop: 10,
          });

          // Handling Audio Playback if Controls are Enabled
          if (audioDiv) {
            const synthControl = new abcjs.default.synth.SynthController();
            synthControl.load(`#${audioId}`, null, {
              displayLoop: true,
              displayRestart: true,
              displayPlay: true,
              displayProgress: true,
              displayWarp: true,
            });

            const createSynth = new abcjs.default.synth.CreateSynth();
            createSynth
              .init({ visualObj: visualObj[0] })
              .then(() => {
                synthControl.setTune(visualObj[0], false);
              })
              .catch((error) => {
                console.warn("Audio synthesis initialization failed:", error);
              });
          }
        });
      }

      initializeAbcjs();
      document.addEventListener('astro:page-load', initializeAbcjs);
    });
  }
</script>

Step 4: Adding Custom Styles

Finally, I added some basic styling to ensure the music notation and audio controls look good on the page and are responsive. You can customize this further to fit your design guidelines.

<style>
  .abcjs-container {
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
  }

  #paper {
    width: 100%;
    min-height: 200px;
  }

  #audio {
    width: 100%;
    margin-top: 20px;
  }

  :global(.abcjs-inline-audio) {
    max-width: 100%;
  }

  :global(.abcjs-btn) {
    margin-right: 0.5rem;
  }
</style>

Step 5: Integrate ABCjsPlayer in an Astro Page

Here’s we can implement the AbcjsPlayer component you’ve created inside any Astro page or markdown file. Just pass in your desired ABC notation and optional props.

//Music Post.mdx
---
layout: ../../layouts/post.astro
title: Step 5: Usage Example in an Astro Post or Page
description: Step 5: Usage Example in an Astro Post or Page 
dateFormatted: 30.10.2024
---
 
import AbcjsPlayer from './components/AbcjsPlayer.astro';

<AbcjsPlayer
notation={`X:1
T: Cooley's
M: 4/4
L: 1/8
K: Emin
|:D2|"Em"EBBA B2 EB|~B2 AB dBAG|"D"FDAD BDAD|FDAD dAFD|
"Em"EBBA B2 EB|B2 AB defg|"D"afe^c dBAF|"Em"DEFD E2:|
|:gf|"Em"eB B2 efge|eB B2 gedB|"D"A2 FA DAFA|A2 FA defg|
"Em"eB B2 eBgB|eB B2 defg|"D"afe^c dBAF|"Em"DEFD E2:|
`}
/>

🔗 Flexible Integration

This component can be easily integrated into any Astro page or markdown file, making music notation accessible across your project.

Next Steps

Now that you’ve built a simple but powerful music component, consider expanding its features. You could support additional file formats, add more customizable controls, or enable export to different music notation formats. Drop a comment below or share your improved version with the community!

If you’re new to Astro, make sure to check out the official Astro Documentation. Happy coding!

Stay up to date

Get notified when I publish something new, and unsubscribe at any time.

Join 44 other subscribers.

© 2025 Pavlin

Instagram GitHub