Vue 3 apps built with Vite compile to static HTML, CSS, and JavaScript that can be served from a CDN with no Node process running. The entire application ships to the user's browser on the first request, then renders client-side as a single-page app. This architecture is fast, cheap, and scales to millions of users without autoscaling containers or managing server state.
The deploy button is how Vue component libraries, starter templates, and demo apps distribute themselves. Instead of documenting a five-step deploy process that users skip, you embed a badge in the README. Clicking it opens a pre-filled deploy screen with the repository URL, build command, and output directory already configured. The user confirms, waits 60 seconds, and gets a live URL to their own instance of your app.
Why a README button is better than deploy instructions
Documentation drift is inevitable. You write "clone the repo, install dependencies, configure the build, deploy to your platform" in the README, then update your build command six months later and forget to update the instructions. Every new user hits the outdated steps, wastes time debugging, and some percentage gives up.
A deploy button encodes the configuration in a URL. When you change the build command, you update the URL query parameters in the README, and every future deploy uses the new command. There is nothing for users to configure unless you explicitly want them to override values.
This is the Vercel model: their deploy button URLs work on PandaStack with a domain swap because the query-string format is compatible.
Add the deploy button to your README
The button is a Markdown image linked to the deploy screen with query parameters that pre-fill the form:
[](https://dashboard.pandastack.io/deploy?repo=yourname/vue3-starter&type=static&buildCmd=npm%20run%20build&outputDir=dist)Query parameters:
repo(required): GitHub repository inowner/repoformattype:staticforces static site detection instead of guessingbuildCmd: URL-encoded build command (npm run buildbecomesnpm%20run%20build)outputDir: directory that Vite writes the production build to (defaultdistfor Vue)branch: defaults tomain
When someone clicks the badge, they land on https://dashboard.pandastack.io/deploy with those values locked into the form. If they are not logged in, they authenticate with GitHub, then proceed to the deploy screen. The deploy screen shows the repository, branch, and build settings. They can add environment variables if needed, then click Deploy.
The build runs, the static files are uploaded to CDN storage, the edge cache is purged, and a live URL appears. The entire process from clicking the badge to a working site takes under five minutes, most of which is build time.
Configure Vite for production builds
Vue 3 with Vite defaults to a production build in the dist/ directory when you run npm run build. The output is static files: index.html, hashed JavaScript bundles, CSS, and any public assets. This works out of the box for most projects.
If your app uses client-side routing via Vue Router, the server needs to serve index.html for all routes so the router can take over. PandaStack's static hosting handles this automatically: any request that does not match a file serves index.html, which is the standard SPA fallback behavior.
Check your vite.config.js to confirm the build.outDir matches the outputDir parameter in your deploy button:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
build: {
outDir: 'dist',
},
});If you changed outDir to build or something else, update the deploy button URL to match. The platform looks for the directory you specify; if it cannot find it, the deploy fails with "Could not find a production build."
Prompt for environment variables with pandastack.json
Vue apps often need environment variables at build time: API endpoints, feature flags, analytics IDs. Vite injects variables prefixed with VITE_ into the client bundle during the build, so import.meta.env.VITE_API_URL becomes a string literal in the compiled code.
If you hard-code these variables in .env and commit the file, anyone who forks your repo inherits your values. If you omit the file, their build succeeds but the app is broken because the variables are undefined.
pandastack.json in the repo root solves this. Its env array tells the deploy screen to prompt for values before starting the build:
{
"type": "static",
"name": "vue3-starter",
"buildCommand": "npm run build",
"outputDir": "dist",
"env": [
{
"key": "VITE_API_URL",
"description": "Backend API endpoint (e.g., https://api.example.com)"
},
{
"key": "VITE_ANALYTICS_ID",
"description": "Google Analytics measurement ID (optional)",
"value": ""
}
]
}When a user deploys, the screen shows two input fields with the descriptions as help text. They fill in the API URL and optionally the analytics ID, then click Deploy. The build receives those variables as environment values, Vite injects them into the bundle, and the app boots correctly on the first try.
This is the same behavior Vercel uses, and it is the cleanest onboarding experience for templates and starters.
Why static sites are faster than container apps
Container apps run a process inside a pod. When a request arrives, it hits the load balancer, gets routed to a pod, the process handles it, and the response streams back. If the app is idle and scales to zero, the orchestrator schedules a pod, pulls the image, starts the process, waits for a health check, then proxies the request. That sequence adds latency.
Static sites skip all of it. The files live in CDN storage. When a request arrives, the edge node serves index.html from cache — there is no container, no process, no health check. The first request and the millionth request have the same latency because nothing is executing server-side.
The trade-off is that static sites cannot handle POST requests, run server-side logic, or connect to a database at request time. If your Vue app only talks to external APIs from the browser, static is the right choice. If you need server-rendered pages or API routes, you need a container app or a separate backend.
Update the deploy button when you change the build config
Your build command will change over time: you might switch from npm to pnpm, add a pre-build script, or change the output directory. Every time you change the build configuration, update the deploy button URL in the README to match.
For example, if you switch from npm run build to pnpm build, the URL becomes:
[](https://dashboard.pandastack.io/deploy?repo=yourname/vue3-starter&type=static&buildCmd=pnpm%20build&outputDir=dist)The old URL continues to work for existing deployments, but new users get the updated command. There is no version skew because the configuration is in the URL, not in documentation that can go stale.
Add a custom domain after the initial deploy
The deploy button creates a project with an auto-generated subdomain like vue3-starter-abc123.pandastack.app. For production use, you want a real domain. In the project settings, add your custom domain, then point a CNAME record at the target PandaStack provides.
SSL certificates provision automatically via Let's Encrypt. Once DNS resolves, the site is live at your domain. The old subdomain continues to work as an alias, which is useful for sharing preview links or testing rollbacks.
Redeploy on every commit with autoDeploy
Static sites are only as fresh as the last build. If you edit a component, commit, and push, the live site does not update until you redeploy. Enabling autoDeploy triggers a new build every time the branch receives a commit.
You can set this in the dashboard under project settings, or via the API when creating the project:
curl -X POST https://api.pandastack.io/v1/projects \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"slug": "static",
"name": "vue3-starter",
"repositoryName": "yourname/vue3-starter",
"branch": "main",
"autoDeploy": true,
"buildCommand": "npm run build",
"outputDir": "dist"
}'Every push to main rebuilds the site, purges the CDN cache, and deploys the new version. The workflow becomes: edit code, commit, push, wait 60 seconds, see the changes live.
References
- [Vue.js production deployment guide](https://vuejs.org/guide/best-practices/production-deployment.html)
- [Vite build documentation](https://vitejs.dev/guide/build.html)
- [PandaStack deploy button parameters](https://docs.pandastack.io/projects/deploy-button/)