PosterShop

2019
2 hours
Frontend Development
Engineering a High Performance E-Commerce Storefront with Vue 3 and Pinia
Most developers think building an e-commerce site is just about listing products and adding a "Buy" button. But the real challenge lies in the perceived performance. Users don't want to see "Loading..." every time they filter a movie category or return from a checkout page. In building this poster shop, I set out to solve the fragmentation between three pillars: Remote Data (API), Local State (the Cart), and External Handshakes (Payments).
The Architecture: The "Reactive Storefront" Model
To keep the application snappy and maintainable, I moved away from the "fetch-on-mounted" pattern and adopted a more sophisticated mental model using a modern Vue stack:
View Layer: Vue 3 (Composition API) + TailwindCSS for a utility-first, responsive UI.
Server State: > @tanstack/vue-query to handle caching, background refetching, and API synchronization.
Client State: Pinia for managing the persistent shopping cart.
Type Safety: TypeScript to ensure our movie metadata doesn't break components downstream.
The app treats the Movie Database API as a single source of truth for "Read" operations, while Pinia acts as the local orchestrator for the "Write" (Cart) operations.
Deep Dive: Orchestrating API & State
The core logic involves fetching movie data and transforming it into "Poster" entities. By using Vue-Query, I eliminated the need for manual loading = true flags across components.
Technical Insight: Using staleTime and cacheTime allowed the poster list to remain in memory, making navigation between "Poster Info" and the "Home" gallery instantaneous.
The Poster Card component serves as the atomic unit of the UI. It doesn't just display data; it interacts with the Pinia useCartStore to handle real-time inventory feedback before the user ever hits the checkout.
The Stripe "Integration Wall"
Every project has a "wall." Mine was the payment integration. Initially, I attempted to use the native stripe-js library. On paper, it’s straightforward. In practice, getting the Stripe elements to play nice with the Vue component lifecycle led to a series of "undefined" errors during the mounting phase and issues with the secure redirect flow.
The Pivot: Instead of fighting the DOM reconciliation, I shifted to @vue-stripe/vue-stripe.
The Lesson: Sometimes, "Vanilla" isn't better. Using a wrapper that understands the Vue lifecycle (specifically the setup and unmounted hooks) turned hours of debugging into a 15-minute implementation. It reminded me that in a production environment, interoperability is just as important as the feature itself.
Results:
The focus on performance wasn't just theoretical. By leveraging Tailwind's tree-shaking and Vue-Query’s caching, the application achieves a "Green Streak" on Lighthouse.
Key Takeaways:
State Separation: Don't put API data in Pinia if it doesn't need to be global and mutable. Use Vue-Query for server state.
Type Everything: TypeScript caught three potential "undefined" errors in the Poster Card component during the Movie API integration.
Packages Matter: Don't be afraid to pivot to a framework-specific library if the native implementation fights the framework's reactivity.
The next milestone
This project was a sprint, but the marathon continues. The roadmap for Version 2.0 includes:
Custom API & DB: Moving away from public movie APIs to a custom backend for personalized poster uploads.
Customizer Tool: A canvas-based UI where users can "quote" a custom design.
Authentication: Integrating to save cart history across devices.



