our-journey-to-a-streamlined-supplier-portal-navigating-the-nuxt3-migration-with-iframes
Engineering
Feb 27, 2025

Our journey to a streamlined Supplier Portal: Navigating the Nuxt3 migration with iFrames

Rositca Yancheva
Software Engineer

Our supplier-facing application, where all GetYourGuide service providers create and manage their products, has a robust tech stack featuring Vue3 within the Nuxt framework. State management is handled by Pinia stores and the PrimeVue component library. This setup has significantly streamlined our development and feature deployment processes. However, transitioning to this point was no easy feat.

Background 

In late 2023, our application was still running on Vue2. With the end of life for this version approaching, we needed to upgrade. Vue3 introduced an improved way of writing components with the Composition API, and we were eager to embrace the change. But why stop here? We also saw this as an opportunity to adopt the Nuxt framework. That also meant transitioning from our state management library, Vuex, to Pinia, which is better supported with this setup. 

As the complexity of the transition grew, we discovered it would not be possible to migrate our code piece by piece; instead, we would require a full rewrite of the application. As if this task wasn’t challenging enough, we also did not have E2E tests to give us confidence in the hard days to come. 

We faced a tough decision:

  • Halt all product development to focus solely on the tech debt, 
  • Continuing to deliver product features by duplicating the work in two projects

OR

  • Create a hybrid solution where the new version runs as a microfrontend within the old application, allowing us to migrate the code incrementally

To make the right decisions, we weighed the pros and cons of each option. While focusing solely on the migration seemed the most straightforward solution from an engineering perspective, it just didn’t work from a product perspective, especially with several high-stakes projects on our plate. Another primary concern was our system stability — thousands of suppliers use our platform daily to run their businesses, with some relying solely on GetYourGuide. 

We needed to complete this process with minimal user disruptions, and launching an entirely new app was simply too risky. Opting for the third option allowed us to narrow the scope of our releases, reduce risks, and respond faster to any issues. Once a piece of code was migrated, product development could continue without any delays in features. Choosing the micro frontend solution also meant no code duplication — a page would always live either in the new or old version but never in both. Despite encountering both expected and unexpected issues, the lessons learned have been invaluable.

The Problem 

Creating a tour on GetYourGuide isn’t a simple task — it’s an intricate journey that ensures a smooth experience for our suppliers. They must consider various questions: Is this a tour or a ticket? Which destinations will be featured? How will travelers reach these locations? The answers to these questions guide the customization of the creation process. For instance, we only inquire about the languages offered if a tour guide is involved, ensuring relevance and efficiency.

Maintaining consistency in global values such as step validity, progress tracking, and conditional elements such as the above-mentioned is crucial throughout the user journey. While migrating standalone pages can be managed with simple redirects between applications, the creation wizard, with its multitude of steps, demanded special treatment. 

The Solution

Each page in our application has its own store that handles its state and updates key properties on our global product creation store. Our migration process involved selecting a page, recreating it in our new app, and establishing a Pinia store structure identical to the old Vuex one.

When a page loads, the current state from the old app is transferred to the iFrame component as a JSON message via the window’s postMessage() API. After a user finishes editing and clicks save, the iFrame sends a message back to the parent app, ensuring seamless state synchronization between the old and new systems. If you are using the code snippet as a blueprint, make sure to replace the wildcard symbol with your own targetOrigin value. 

So far, so good. But what about when the page size is dynamically changing? We needed the iFrame container to respond to size changes, as longer pages would otherwise get cropped. We solved this issue using the same message-sending principle plus a ResizeObserver to monitor for changes. 

Much better! 

This approach allowed us to separate the tech debt work from product development. We identified the pages that needed changes early on, migrated them with priority, and then added new features on a clean slate. The downside was the need to write additional logic to support the iFrame solution and a slight performance penalty. Even though the parent application bundle was cached, the app was reinitialized with each page change, which was not ideal. We accepted it as a temporary solution.

This approach was carefully considered for other teams. We maintained a high level of synchronization through various mediums.

  • Weekly department meetings: These meetings, complete with an agenda and moderator, fostered better collaboration between teams and facilitated the sharing of knowledge and best practices for the project.
  • Responsive Slack Channel: This channel allows for easy sharing and searching of common errors, tips, and tricks.
  • Cross-team PR reviews: This ensured that all code changes were thoroughly vetted and aligned with the overall project goals.
  • Tech sharings: Regular tech sharing sessions helped disseminate important information and innovative solutions across teams.

We ensured a smooth and coordinated migration process by leveraging these communication and collaboration tools.

Learnings 

Navigation with iFrames is hacky

While most interactions followed a straightforward flow, some required more intricate handling, such as full-page redirections. For security reasons, you can’t change the page URL from inside an iFrame, so we had to manage those redirects through our iFrame-parent messaging system — adding another layer of complexity to navigation.

Testing the iFrame integration is tricky

Although we could test individual components independently, the communication between iFrames and the parent component required end-to-end (E2E) tests. This limitation reduced our confidence in the tests and moved the testing safety net closer to the release step than we would have preferred. As a result, we had to rely more heavily on thorough E2E testing to ensure the reliability and functionality of the iFrame integration before deployment.

Involving non-technical stakeholders in testing

At first, our team ran bug bashes independently, but we soon realized we weren’t catching everything. Involving colleagues from the Content team, who were excellent at spotting bugs in edge cases we missed, proved invaluable. Their fresh perspective helped us catch issues early, preventing several bugs from making it to production.

These learnings underscore the importance of collaboration and early involvement of all relevant stakeholders in complex projects.

Conclusion

Avoiding non-backwards compatible upgrades is ideal, but if necessary, you have to consider your project’s complexity. A complete rewrite and release might be the better option for more straightforward projects since creating the synchronization of iFrame required infrastructure can be a significant effort.

A complete rewrite becomes progressively riskier for more complex systems with many nested flows and stages. In such cases, an iFrame-embedded approach can offer a great alternative, providing flexibility and peace of mind during releases. Ultimately, it’s all about carefully weighing the trade-offs to find the best path forward.

Other articles from this series
No items found.

Featured roles

Marketing Executive
Berlin
Full-time / Permanent
Marketing Executive
Berlin
Full-time / Permanent
Marketing Executive
Berlin
Full-time / Permanent

Join the journey.

Our 800+ strong team is changing the way millions experience the world, and you can help.

Sign up to our newsletter

Oops! Something went wrong while submitting the form.