Deploy Popover
The Deploy Popover component provides a popover interface for publishing a deployment to Vercel. It calls the Deploy Files action, tracks the deployment's status in real time, and links to the deployment once it's ready.
Install the deploy-popover block with the Vercel Platforms CLI:
npx @vercel/platforms@latest add deploy-popoverYou can also install it with the shadcn CLI:
npx shadcn@latest add https://registry.platforms.guide/deploy-popover.jsonThe block imports deployFiles and getDeploymentStatus from ../actions/deploy-files. Install and configure the Deploy Files action too, including its Vercel client and environment variables.
- One-click deployment: A Publish to Production button triggers a production deployment
- Real-time status tracking: Monitor deployment progress with live updates
- Deployment states: Visual feedback while the deployment starts, builds, and becomes ready
- Direct deployment access: A View your deployment button opens the deployment URL in a new tab when it's ready
- Automatic polling: Built-in SWR polling for deployment status updates
- Domain and visibility views: UI for choosing a
.vercel.appsubdomain and public or private visibility, which you connect to your own logic
import { DeployPopover } from '@/components/blocks/deploy-popover';
export default function MyComponent() {
return (
<div className="flex items-center justify-center p-8">
<DeployPopover />
</div>
);
}The component renders a Publish button that opens the popover, and manages several deployment states:
Initial state before any deployment action. Shows the Publish to Production button.
The deployFiles call is in progress. The button is disabled and shows "Starting deployment..."
Checking deployment status after the deployment is created. The button is disabled and shows "Building application...", "Initializing deployment...", or "Checking deployment status..."
Deployment successfully completed. Shows the View your deployment button.
The deployment's readyState is ERROR. The state carries the message "Your deployment failed.", but the installed block doesn't render it. The button returns to Publish to Production so the user can retry. Render state.message in the block if you want to show the error.
The block is copied into your project as source code, so you customize it by editing the installed file.
The popover doesn't define the files it deploys. It calls deployFiles, and the installed action deploys a hardcoded sample index.html file. To deploy different files, edit the action as shown in Deploy your own files, then pass the files in the popover's deployFiles call.
The popover doesn't choose a project. The Deploy Files action deploys to the project in the DEPLOY_FILES_PROJECT_ID environment variable, or to a project named after the deployment name when that variable isn't set.
Customize the deployment name in the deployFiles call inside the block's useDeployment hook:
const { trigger: deploy, isMutating: isDeploying } = useSWRMutation(
'/api/deploy',
() =>
deployFiles({
deploymentName: 'your-custom-deployment-name',
}),
);The Customize Domain and Visibility views store their values in component state only. The installed block doesn't pass either value to deployFiles, and its handleAddDomain handler only returns to the main view. To apply the chosen subdomain, pass it to deployFiles as the domain option, for example by passing customDomain into useDeployment.
The block calls the Deploy Files Server Actions like this:
import { deployFiles, getDeploymentStatus } from '../actions/deploy-files';
// Deploy files to Vercel
const result = await deployFiles({
deploymentName: `platforms-deploy-test-${Date.now()}`.toLowerCase(),
});
// Check deployment status
const status = await getDeploymentStatus(result.id);The component uses SWR for automatic status polling with these defaults:
- Refresh interval: 10 seconds
- Error retry count: 3 attempts
- Error retry interval: 2 seconds
You can adjust these in the useSWR configuration:
const REFRESH_INTERVAL = 10_000; // 10 seconds
useSWR(
deploymentId ? deploymentId : null,
(nonNullDeploymentId) => getDeploymentStatus(nonNullDeploymentId),
{
refreshInterval: deploymentId ? REFRESH_INTERVAL : 0,
revalidateOnFocus: false,
shouldRetryOnError: true,
errorRetryCount: 3,
errorRetryInterval: 2000,
},
);Was this helpful?