Aline.js x-data not reactive

I’m using Alpine.js to render a list of videos from an API. After making the API call, the response data populates my videos array successfully, but the page doesn’t update to show the new data.

Here’s a breakdown of what’s happening:

I’m making an Axios API request and pushing the resulting videos to the videos array.
I’ve confirmed via console log that videos is populated with the correct data.
Despite this, the template does not render the updated array.

Thank you!

  <template x-for="video in videos" :key="video.id">
    <!--video content-->
    <div>Test Video</div>
  </template>
      const app = {
            triggerElement: null,
            page: 1,
            lastPage: 24,
            itemsPerPage: 24,
            observer: null,
            isObserverPolyfilled: false,
            videos: [],
            debug: true,
            loaded: false,

            init: function () {

                window.addEventListener('urlChange', () => {
                    app.getItems();
                });

                app.triggerElement = document.querySelector('#infinite-scroll-trigger');

                document.addEventListener('DOMContentLoaded', function () {
                  //  app.getItems();
                    app.loaded = true;
                });

                app.infiniteScroll();

                (app.debug) ? console.log('init' )  : '';


            },
            infiniteScroll: function () {

                (app.debug) ? console.log('infiniteScroll' )  : '';

                // Check if browser can use IntersectionObserver which is waaaay more performant
                if (!('IntersectionObserver' in window) ||
                    !('IntersectionObserverEntry' in window) ||
                    !('isIntersecting' in window.IntersectionObserverEntry.prototype) ||
                    !('intersectionRatio' in window.IntersectionObserverEntry.prototype)) {
                    // Loading polyfill since IntersectionObserver is not available
                    this.isObserverPolyfilled = true

                    // Storing function in window so we can wipe it when reached last page
                    window.alpineInfiniteScroll = {
                        scrollFunc() {
                            var position = app.triggerElement.getBoundingClientRect()

                            if (position.top < window.innerHeight && position.bottom >= 0) {
                                if (app.loaded) {
                                    (app.debug) ? console.log('getItems 1' )  : '';

                                    app.getItems();
                                }

                            }
                        }
                    }

                    window.addEventListener('scroll', window.alpineInfiniteScroll.scrollFunc)
                } else {
                    // We can access IntersectionObserver
                    this.observer = new IntersectionObserver(function (entries) {
                        if (entries[0].isIntersecting === true) {
                            if (app.loaded) {
                                (app.debug) ? console.log('getItems 2' )  : '';

                                app.getItems();
                            }
                        }
                    }, {threshold: [0]})

                    this.observer.observe(this.triggerElement)
                }
            },
            getItems: function () {
                // TODO: Do fetch here for the content and concat it to populated items
                // TODO: Set last page from API call - ceil it

                let currentURL = new URL(window.location.href);
                currentURL.hostname = 'api.' + currentURL.hostname;


                axios.post(currentURL, {
                    page: this.page,
                    perPage: this.itemsPerPage,

                })
                    .then(response => {
                        const data = response.data;
                        //  this.lastPage = data.total_pages;
                        app.videos = data.data.videos;
                        //  this.page++;
                        (app.debug) ? console.log(response)  : '';

                    })
                    .catch(error => {
                        console.error('Error loading videos:', error);
                        (app.debug) ? console.log(error)  : '';
                        this.loading = false;
                    });


                // Next page
                this.page++

                // We have shown the last page - clean up
                if (this.lastPage && this.page > this.lastPage) {
                    if (this.isObserverPolyfilled) {
                        window.removeEventListener('scroll', window.alpineInfiniteScroll.scrollFunc)
                        (app.debug) ? console.log('alpineInfiniteScroll')  : '';

                    } else if (this.observer && this.triggerElement) {
                        try {
                            this.observer.unobserve(this.triggerElement);
                        } catch (e) {
                            console.error('Failed to unobserve element:', e);
                        }
                    }

                    if (this.triggerElement && this.triggerElement.parentNode) {
                        this.triggerElement.parentNode.removeChild(this.triggerElement);
                        this.triggerElement = null; // Prevent further access to the removed element
                    }

                }
            }
        };

Using this.$nextTick() after updating videos to ensure Alpine.js updates.
Manually triggering Alpine.js reactivity.
Directly manipulating the DOM as a temporary workaround (not ideal).
None of these approaches resolved the issue. I’d appreciate any insight into why Alpine.js might not be re-rendering the updated data or any alternative solutions.