A demo HubSpot private app built on the 2026.03 developer platform. It
showcases app pages with paginated routing, serverless functions calling both
external (PokéAPI) and HubSpot CRM APIs, and a small UI that turns each CRM
contact into a Pokémon "rating."
- An app pages extension with five routes: Home, Pokédex, a Pokémon detail page, a Catalogue, and a Trainer Guide.
- Three serverless functions — two for PokéAPI lookups, one for reading CRM contacts.
- A static-auth private app scoped for reading and writing contacts.
- A HubSpot account in which you can install a private app (a developer test account works).
- Node.js 22+ — required by platform version
2026.03. - HubSpot CLI
8.4.0or newer:npm install -g @hubspot/cli hs --version
-
Clone the repo and enter it:
git clone https://github.com/hubspotdev/dev_pg_2026q2_AppPages.git cd dev_pg_2026q2_AppPages -
Authenticate the CLI against your HubSpot account. Follow the prompt to generate a Personal Access Key and paste it back:
hs account auth hs account info # OPTIONAL: to check which portal you're pointed at -
Install local dependencies
hs project install-deps
-
(Optional — only needed for the Trainer Guide page) Create a custom contact property called
demo_contactso the page can scope its lookup to demo data:- HubSpot → Settings → Properties → Contacts → Create property
- Internal name:
demo_contact, Field type: Single checkbox, label: "Demo Contact" - A CSV file sample-contacts.csv is available with demo contacts that you can upload to your test portal using the HubSpot Import Tool
- Alternatively tag a handful of contacts with
Demo Contact = Yes, and make sure they have a Job title and Lead status set so the search function in src/app/pages/TrainerGuidePage.tsx has something to match against.
-
Upload and deploy the project to the account you just authed with:
hs project upload
The CLI bundles the project, builds it, and (by default) deploys it. On the first upload, the app is also installed in the account.
-
Open the project in HubSpot. Can be done via the browser or via terminal:
hs project open # Opens the project's page in the HubSpot UI.From there click into the app — the Overview page opens first, with Overview, Auth and Distribution tabs.
-
Install the App. To initialise the app in your portal you first need to install it.
- Select the Distribution tab. You will see the status of the app in your portal.
- Click the Install Now button beside your account.
- You will be brought to an app verification page. Check the checkbox and click Connect App.
-
(Optional) You can make updates locally while rendering the page:
hs project dev
While that's running, UI extensions show a "Developing locally" tag in HubSpot, and saving files locally re-renders the page immediately. You still need
hs project uploadafter editing to update the project in your portal. See the hs project dev documents for more.
The project follows the standard 2026.03 directory layout for private apps:
| Feature | What it does | Where it lives |
|---|---|---|
| Project config | Declares the project name, source dir, and platform version. | hsproject.json |
| App component | The private app: distribution, auth type, required scopes, and allowed URLs for hubspot.fetch and image URLs. |
src/app/app-hsmeta.json |
| App pages extension | A React multi-page resource inside HubSpot, with in-app navigation. | src/app/pages/pages-hsmeta.json (config), src/app/pages/Pages.tsx (page router) |
| Page components | One file per route, plus a shared EvolutionChain component and the static pokemon.json list. |
src/app/pages/ |
| Serverless functions | Server-side handlers invoked via hubspot.serverless from the pages. Each function has a JS entrypoint and a *-hsmeta.json config that defines its uid. |
src/app/functions/ |
All routes are in src/app/pages/Pages.tsx.
| Route | Component | What it shows |
|---|---|---|
/ |
HomePage | Welcome page with the current HubSpot user's first name, plus an AutoGrid of tile links into each feature. |
/pokedex |
PokedexPage | Select a Pokémon → the page calls a serverless function that fetches its evolution chain and base stats from PokéAPI. |
/pokedex/:name |
PokemonDetailPage | Larger artwork plus a detail table (types, weaknesses, strengths, moves, abilities). Backed by a separate serverless function. |
/catalogue |
CataloguePage | Grid view of every Pokémon in pokemon.json. |
/trainer-guide |
TrainerGuidePage | CRM contact lookup. The page calls a serverless function that returns demo contacts; the selected contact's hs_lead_status and jobtitle are each mapped to a Pokémon rating. |
/docs |
DocsPage | Placeholder documentation page. |
| Function | uid |
Purpose |
|---|---|---|
| PokemonFamilyLookup.js | pokemon_family_lookup |
Fetches a Pokémon's evolution chain and basic stats from PokéAPI. Called by PokedexPage. |
| PokemonDetail.js | pokemon_detail |
Fetches a Pokémon's full profile (types, weaknesses, moves) from PokéAPI. Called by PokemonDetailPage. |
| ContactsLookup.js | contacts_lookup |
Reads CRM contacts marked demo_contact = true via the HubSpot CRM v3 API, using the auto-injected PRIVATE_APP_ACCESS_TOKEN secret. Called by TrainerGuidePage. |
hubspot.fetch is used for calling the backend. Calls into
HubSpot's own CRM API can happen inside serverless functions, where
the app's installed access token is available as process.env.PRIVATE_APP_ACCESS_TOKEN.
| Command | What it does |
|---|---|
hs project upload |
Bundle, build, and deploy the project to the connected portal. |
hs project deploy |
Deploy a previously uploaded build. |
hs project dev |
Local dev server to make inline edits to UI extensions. |
hs project list-builds |
List recent builds for the project. |
hs project logs |
Stream logs for the deployed serverless functions. |
hs project open |
Open the project's page in the HubSpot UI in your default browser. |
For more on HubSpot CLI commands see: HubSpot CLI commands overview