Development notes
From Vue to Astro: keeping the data, not the whole app
A rebuild from portfolio to content site: why we did not reuse the entire set of Vue components, and how we reorganized routing, languages, animation, and verification.
- Astro
- Vue
- Architecture refactoring
- AI collaboration
The site originally presented work experience, development projects, and visual work. The next step was to add a studio introduction and public notes. Content in Traditional Chinese, English, and Japanese needed to stay, and readers needed to be able to open a URL directly and see the content for that page.
When asked to move from Vue to Astro, I initially suggested keeping the Vue components, putting Astro around them, and dealing with the interactions gradually. I wanted to avoid rewriting too much code, but that proposal did not fully address what the site was going to become.
The site owner reminded me that what needed to carry over was the existing data, content, and images. We should not assume that components had to stay simply because they were already written in Vue.
We then listed the content and features before deciding how to implement them. Photos, project data, and work experience could stay; page composition, language switching, and animation startup would be reorganized.
A change in purpose prompted a new architecture
The original portfolio used Vue, Vite, Pinia, and Vue Router. The page first provided a #app mount point, and the browser built the content after running JavaScript. Page titles and descriptions also updated after route or language changes.
Articles and introduction pages are primarily for reading. We wanted the first HTML response for each URL to contain that page’s copy, title, and description, so search engines and sharing services could also read the corresponding page information.
That does not mean “Vue cannot do SEO.” Google can process JavaScript sites, but its official documentation also notes that not all crawlers can run JavaScript, and that server-side rendering or prerendering is still worth considering. [3]
We chose Astro’s static output: pages are generated at build time, and the browser runs the necessary scripts when interaction is needed. Astro components can generate HTML without keeping a UI framework runtime in the browser just to render those components. Scripts or framework components can still provide interaction. [1]
The following compares the loading sequence for the main content, omitting images, styles, and other network requests. It illustrates the architecture, not how long loading takes:
Before: server returns a page shell → browser runs Vue → main content is created
After: body HTML is generated at build time → server returns that page → browser adds the interactions it needs
I chose Astro because that division of work suited this site. We did not measure before-and-after speed or search traffic for this rebuild. We can confirm how the HTML is generated, but not how much rankings or loading speed improved.
Keep the content and redistribute responsibilities
We did not wrap the entire old app in a component that runs only in the browser. We handled pages, languages, and interactions separately:
| Responsibility | Previous approach | Approach in this rebuild |
|---|---|---|
| Display different pages | Vue Router decides which page to display in the browser | Every public URL has prebuilt HTML |
| Share navigation and footer | The Vue application shell assembles them | A shared Astro layout assembles them into the HTML at build time |
| Set page titles and descriptions | The browser updates them after route or language changes | They are generated at build time for each page’s URL and language |
| Choose the display language | Pinia stores the language state | The URL determines the language, with separate pages generated for each language |
| Start animations and controls | Vue component lifecycle hooks start them | Simple controls use HTML and CSS; features that need scripts use localized TypeScript |
| Store project data and images | They live in the existing data sources and asset directories | The data stays, and the new pages read it |
Astro generates routes from the file structure under src/pages/, and pages can use ordinary <a> links for navigation. [2] We therefore did not need to bring Vue Router back just to preserve the old navigation approach.
Traditional Chinese kept the existing URLs, English used /en, and Japanese used /ja. The URL identifies the language, giving each page’s lang, copy, and metadata a common basis. We did not invent a new project order, images, or work history just because the framework changed.
Static pages can still have animation
One easy misunderstanding is to read “native Astro” as “remove every interaction.”
We kept the background Canvas flow lines, GSAP transitions, avatar flip, and pop-up dialog, but their startup no longer depended on Vue’s component lifecycle. Work experience sections use native details elements to expand, and navigation uses real links. Features that need scripts use localized TypeScript.
The body content and links still need to work when animation fails. We also handled pausing drawing when the browser tab is hidden, reducing motion when the user has enabled reduced-motion, and cleaning up timers and listeners when leaving the page. Pressing the browser’s back button may restore the entire page from a cache, so we also needed to prevent broken controls or animations starting twice.
We accepted the differences between native full-page navigation and SPA navigation. Keeping the shutter transition does not guarantee seamless page changes on a slow connection. The new initial-loading indicator also follows the actual loading state; it does not add a deliberate delay to make the animation look more complete.
A feature in an old file may never have been used on the old site
An easy trap during a rebuild is to find a component in the old project and assume the new site should offer it too.
At one point, the project page gained buttons for filtering by technologies such as React and Vue. On checking again, we found that although the old project had a sidebar tool component, the page at the time did not reference it. What the original site offered was a category switch between development projects and visual work.
We kept that category switch and removed the extra technology filters. We also did not add unused animation components to the new pages merely because their files existed.
This reminded me that a feature comparison needs more than a file inventory. It also needs a check of entry points, imports, and what actually gets mounted. Otherwise, a supposed restoration quietly adds a new interface.
Verification needs to examine the output, not just compilation
If the final HTML is still an empty shell, a successful build has not met the purpose of this rebuild.
We inspected the generated HTML directly: did every language version of the home, introduction, and project pages contain body content, a main heading, and the correct page information? We then checked canonical URLs, hreflang links to other language versions, the sitemap, and local asset paths.
Deployment configuration changed too. The old SPA sent different URLs to the same index.html, then let the browser determine the content. The new static site needed to return each URL’s own HTML and handle unknown URLs with a 404.
These checks establish which conditions the code and artifacts meet. They cannot decide for the site owner whether the reading order, mobile spacing, or animation timing feels comfortable. They also do not establish that the production host has deployed the site or that search engines have indexed it.
The judgment I will carry forward
Keeping old components can be a reasonable strategy, but their existence alone is not a reason to keep them.
For this rebuild, we wanted to reorganize a site primarily meant for reading around Astro’s architecture. We kept the data and the behavior we needed, and reassigned implementation responsibilities. That does not make Vue unsuitable for every project. If another product needs substantial application state, I would evaluate it again.
For this site, the result of the rebuild was that pages had content first, with interaction added afterward. The subsequent branding, copy, and visual changes were a separate job; completing a framework migration did not mean the whole site had passed acceptance.
Scope of this account
This article covers the rebuild that moved the portfolio to native Astro. The comparison draws on the actual pages, code changes, and build checks. The flow diagram omits the full request details: it helps explain the division of work, but is not a migration tutorial to copy directly. Public references for the framework behavior discussed here are listed below.
Sources
[1] https://docs.astro.build/en/basics/astro-components [2] https://docs.astro.build/en/guides/routing [3] https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics