BlogPost
by: Jonathan Folland February 7, 2025

Using Astro to Wrap Legacy JavaScript Frameworks

Astro has a number of integrations that allow for rendering common front-end frameworks such as React, Angular, Vue, and Svelte as "islands." This is an extremely powerful concept as it allows developers to use Astro as a framework while plugging in front-end frameworks to handle more complex client-side interactivity. The benefit here is obvious to most developers. However, what is not so obvious is that the structure of Astro components can also be leveraged to wrap legacy JavaScript frameworks.

Below, we demonstrate two simple counter components built as Astro components with client-side interactivity using legacy AngularJS and KnockoutJS.

Why Would You Do This?

For new development, you wouldn't want to take this approach. However, consider a scenario where:

  • You have a complex client-side application built with a legacy framework.
  • No one on your team fully understands the old code.
  • There's little budget available for a full rewrite.
  • The application must be migrated to a new environment without major modifications.

Using Astro in such a scenario may allow you to wrap and embed legacy code without completely rebuilding the application.


Astro Component Structure

Understanding the basic structure of an Astro component is key to making this possible. Astro components consist of three possible sections:

---
// Server-side code (optional)
---
<markup/>
<script />

For our KnockoutJS example, the server-side block is empty, but the markup is standard HTML containing Knockout-specific bindings. The script section handles loading the KnockoutJS library and binding it to the elements.


KnockoutCounter.astro

---
---
<divclass="container mx-auto"><divid="knockout-counter-app"class="pb-[50px] md:pb-[60px] lg:pb-[80px] xl:pb-[100px]"><h2class="text-black font-semibold text-[25px] md:text-[30px] lg:text-[32px] xl:text-[36px] leading-[1.2]">Knockout Counter Example</h2><pclass="text-black font-semibold">
      Counter value: <spandata-bind="text: counter"></span></p><p><buttondata-bind="click: increment"class="text-blue-900 underline font-semibold hover:text-[#EF4335]">Increment</button></p><p><buttondata-bind="click: decrement"class="text-blue-900 underline font-semibold hover:text-[#EF4335]">Decrement</button></p></div></div><script>let loadedAndBindKnockout = false;
  const loadAndBindKnockout = () => {
    if (loadedAndBindKnockout) return;
    loadedAndBindKnockout = true;

    const counterApps = document.querySelectorAll("div#knockout-counter-app");
    if (!counterApps || counterApps.length < 1) return;

    console.log("Knockout loading");

    const script = document.createElement("script");
    script.src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js";
    script.onload = () => {
      const counter = ko.observable(0);
      const increment = () => counter(counter() + 1);
      const decrement = () => counter(counter() - 1);

      ko.applyBindings({ counter, increment, decrement }, document.getElementById("knockout-counter-app"));
      console.log("Knockout loaded");
    };
    document.head.appendChild(script);
  };

  if (document.readyState === "complete" || document.readyState === "interactive") {
    loadAndBindKnockout();
  } else {
    document.onload = () => {
      loadAndBindKnockout();
    };
  }
</script>

This will render the KnockoutJS counter inside your Astro project. Below you can click the increment / decrement links to change the count value; controlled by the knockoutjs code.

Knockout Counter Example

Counter value:


We will not display the full AngularJS implementation here as the concept is similar to KnockoutJS. However, the same approach can be used to dynamically load AngularJS and attach it to a specific element inside Astro.

AngularJS Counter Example

Counter value: {{ counter }}

 

This confirms that Astro can handle multiple legacy JavaScript frameworks as separate islands.


Final Thoughts

If you're faced with the challenge of migrating a legacy client-side application into a modern Astro-based environment, this technique might help you preserve functionality while keeping the migration effort minimal.

⚠️ Caution:

  • Some legacy frameworks may conflict with Astro's hydration techniques.
  • Performance considerations should be taken into account, especially if loading large libraries.
  • Script isolation is necessary to avoid interference between multiple frameworks.

If your use case involves maintaining old code while migrating to Astro, this approach may provide a short-term solution before a full rewrite is feasible.


Would you use this technique in your project? Let us know in the comments below!

Share


Harness the power of headless to achieve marketing excellence!

The team at Given Data LLC continuously monitors advances in the content management space, keeping us ahead of the competition. Urgent need? call us

+1 786-475-5504

Contact Us Arrow Right 2

Services

Resources

Opportunities

Newsletter

©2025 Given Data, LLC. All rights reserved.