How to Build and Publish a Polkadot Product on Devnet
Learn how to build a static web app with the Polkadot Product SDK, register a .dot domain, authorize storage, and publish it on the Products Devnet.
On this page
A static React app normally needs a server, a hosting provider, and a domain before anyone else can use it. The Polkadot Products Devnet changes that workflow: you can build a static web app, connect it to Polkadot's host services with the Product SDK, register a .dot name, and publish the finished bundle to the Bulletin chain. This tutorial walks through that path from an empty project to a published Product, while showing where the blockchain-specific pieces actually enter the workflow.
What you are building and where each piece lives
A Product on the Devnet is still a web application made from HTML, CSS, and JavaScript. The unusual part is how it is delivered and how it reaches blockchain services. Bulletin stores the application's published bundle, Asset Hub handles the .dot naming layer and smart contracts, while the Polkadot host provides capabilities such as account access and transaction signing.
That separation matters because you do not start by writing a smart contract. A useful first Product can be a completely static application. When you later need persistent decentralized storage, blockchain state, or contract calls, you add the relevant Product SDK package rather than rebuilding the whole application around a blockchain backend.
The current developer workflow uses three command-line tools. dotns manages .dot names, pad publishes the static application, and cdm handles contracts when a project needs them. For a first application, you only need the first two.
Install Node.js and the Devnet tools first
The current developer documentation requires Node.js 22 or newer for the publishing tools. Check your installed version before doing anything else:
node --version
If that reports Node 22 or a later release, install the naming and publishing command-line tools:
npm i -g @polkadot-community-foundation/dotns-cli
npm i -g @polkadot-community-foundation/polkadot-app-deploy
The first package provides the dotns command. The second provides pad, which turns a built frontend into a publishable Product bundle. You do not need the Contract Dependency Manager yet; that is a separate tool for projects containing PolkaVM smart contracts.
For the application itself, install the Product SDK inside your project:
npm i @parity/product-sdk
The SDK is more than a wallet library. Its packages expose typed interfaces for host access, chain connections, storage, transactions, contracts, identity, and related services. The important distinction is that these services are provided through the Polkadot host rather than by putting private keys into ordinary browser JavaScript.
Start with a normal React and Vite application
The simplest way to learn the workflow is to start with a normal static frontend. React 19, Vite, and TypeScript are used by the current reference template, but the publishing model does not require React. Any frontend that produces a static build directory can follow the same basic delivery path.
For a Vite project, your development cycle remains familiar. Install dependencies, build the application, and check that the build produces a dist directory:
npm install
npm run build
The important result is not the development server. It is the finished static bundle inside dist/. The pad command publishes that directory, so anything your application needs at runtime must be present in the build output.
Connect the app to the Product SDK only when it needs blockchain services
A static application can be published without making a blockchain call. The Product SDK becomes useful when the application needs services supplied by the Polkadot environment. For example, the SDK can create an application object and expose cloud storage functionality:
import { createApp } from "@parity/product-sdk";
const app = await createApp({
name: "my-app",
cloudStorage: {
environment: "devnet"
}
});
const result = await app.cloudStorage!.upload("hello world");
if (result.ok) {
console.log(result.value);
}
The returned value represents the content identifier for the uploaded data. A content identifier, or CID, is a fingerprint-like reference to content rather than a conventional database row ID. The useful consequence is that your frontend can store or retrieve content through the platform without building a separate upload server for that feature.
There is one easy mistake here: the SDK expects to run inside a supported Polkadot host. The reference documentation says that calling the host-dependent application setup outside the Polkadot app or its web gateway can fail because the required host connection is not available. Local development therefore has a different shape from an ordinary browser-only Vite project.
Choose the Devnet explicitly before you touch the chain
The Product SDK and command-line tools support more than one network configuration. For this tutorial, the target is the public Products Devnet, identified by the devnet preset. The publishing commands use --env devnet, while contract commands use -n devnet.
Do not leave the network flag out simply because a command appears to work without it. The developer documentation warns that the tools can have other network presets available, and publishing to the wrong network can leave your name and application somewhere the Devnet cannot resolve them.
This is one of those blockchain-specific details that is easy to miss in a conventional frontend tutorial. A successful command is not necessarily proof that you deployed to the environment you intended.
Create a dedicated signing account before registering your name
The publishing process needs an account that can sign transactions on Asset Hub and own the .dot name being published. For a temporary development project, the documentation provides a throwaway-account workflow rather than requiring you to reuse a valuable wallet.
The CLI can generate a mnemonic and expose it through the current shell session. Once you have an account, check its address and fund that address with Devnet tokens through the available faucet before attempting on-chain operations.
export MNEMONIC="your-development-mnemonic"
dotns account address
The exact account-generation method can vary with the installed CLI version, so the safer rule is to use the tool's current account workflow rather than copying a production wallet into a terminal. Never paste a valuable mainnet seed phrase into a tutorial command or an environment variable on a shared machine.
There is another account detail worth understanding. The dotns CLI has its own signing configuration, while pad can receive the mnemonic explicitly when publishing. They can use the same development account, but the tools do not share a single browser wallet session.
Register a .dot domain before publishing the application
Once the signing account is ready, register a name for the Product. The current Devnet rules require a label with a stem of at least nine characters for the ordinary registration path.
dotns register domain --name my-cool-app --env devnet
Registration is not just a local DNS-style operation. The name becomes part of the on-chain naming system, so the transaction needs time to complete. Verify ownership before moving on:
dotns lookup owner-of my-cool-app --env devnet
The verification step saves time later. If the name is not owned by the same account that will publish the application, the deployment can reach the upload stage and then fail when it tries to bind the new content to the name.
Give the publishing account Bulletin storage authorization
The application bundle is stored on the Bulletin chain, but uploading is not simply a matter of paying a normal transaction fee for every file. The Devnet uses storage authorization and upload quota. The account performing the publication needs permission to write the bundle.
This is separate from the native Devnet token balance. The token balance covers chain operations and fees, while Bulletin authorization determines whether the account can upload content. Keeping those two requirements separate explains a common failure: an account can have Devnet tokens and still be unable to publish because its Bulletin storage authorization is missing.
Before running the final publishing command, make sure the same signing account owns the .dot name and has the required Bulletin authorization. That account is the one that needs the upload quota.
Build the frontend and publish the dist directory
Now perform the normal frontend build:
npm run build
Assuming Vite has produced dist/, publish it with pad:
pad ./dist my-cool-app.dot --env devnet --mnemonic "$MNEMONIC"
This command does more work than copying files to a web server. The publishing tool turns the build into content-addressed data, uploads the required content to Bulletin, obtains a root CID, and updates the on-chain name record so the .dot name points at that content.
That distinction is useful when you change the application. The name does not contain the JavaScript bundle itself. Instead, the name points to the content identifier for the published version. When you publish a new build, the content changes and the name can be updated to point at the new content.
Check the result before adding contracts or other complexity
A successful publication should finish with verification information and the published content identifier. Open the Product through the Devnet's supported application environment and check the actual build rather than assuming that a successful command means every frontend feature works.
If the application does not appear correctly, check the failure in this order. First, confirm that the publishing account owns the .dot name. Second, confirm that the account has Bulletin storage authorization. Third, verify that you built the current source into dist/. Finally, check that the name resolves to the latest content identifier rather than an older deployment.
This order matters because the publishing pipeline has distinct stages. A frontend build problem is different from a storage authorization problem, which is different again from an on-chain naming problem. Treating them as one generic deployment failure makes debugging unnecessarily difficult.
Add smart contracts only when the application actually needs them
Once the static Product works, you can add a smart contract if the application needs persistent on-chain state or contract-controlled logic. On the Products Devnet, these contracts run through PolkaVM on Asset Hub, and the recommended workflow uses the Contract Dependency Manager, or CDM, to build, deploy, publish metadata, and register contracts.
That creates a useful separation in the development process. Your frontend remains responsible for the user interface, while the Product SDK provides typed contract access. CDM handles the contract's lifecycle and lets applications resolve a contract by a package name instead of hard-coding an address throughout the frontend.
The contract workflow has additional prerequisites, including a Rust toolchain. It is therefore better to prove that the static application and publishing path work first. Adding contracts before the basic Product can be deployed gives you two independent sources of failure at once.
The real advantage is the deployment model, not the .dot name
The interesting part of this Devnet workflow is not simply that an application gets a blockchain-themed domain. The delivery pipeline separates the application bundle from the name that points to it. Bulletin stores the content, Asset Hub stores the naming information, and the host provides application capabilities such as accounts and signing.
For a developer, that changes what a small blockchain application can look like. A first project does not need a custom backend merely to get a wallet connection or decentralized storage feature, and it does not need a smart contract simply to become publishable. You can start with the same static frontend workflow used for an ordinary web app and add blockchain services only where the product needs them.
There are still important limits. The Products Devnet is a developer preview, not a substitute for a production deployment strategy, and the current Product SDK repository describes its implementation as experimental and not audited. Devnet tokens also have no real-world value. The sensible next step is therefore to build something small, publish it, inspect the complete path from frontend build to on-chain name resolution, and only then decide which storage, identity, transaction, or contract capabilities deserve a place in the application.
Written by


