Menu
Guides
Storefront Development

Storefront Development

Extending My Account for B2B stores

Learn how to extend the My Account page in your B2B FastStore project.

7 min read
In this guide, you'll learn how to extend the My Account page in your B2B store by creating custom CMS components and adding them to existing pages or to new CMS-managed pages.
Check if the default My Account pages meet your needs. If not, you can create a custom component and then add it to an existing page or use it in a new CMS-managed page. These extensions allow you to customize the My Account experience without changing its default structure.
All My Account routes are private and must include the /pvt/account prefix. Custom routes are validated to ensure this prefix is included, but you should declare it. The validation compares complete path segments, so a route such as /pvt/accounting/dashboard isn't considered part of /pvt/account.

Before you begin

Make sure you have the following:
  • FastStore v4.5.0 or later. See Updating the CLI package version.
    The My Account extension features described in this guide require v4.5.0. If you are on an earlier version, update your package before proceeding.
  • My Account enabled in your project, as described in Enabling My Account for B2B stores. Without the enableFaststoreMyAccount flag, all /pvt/account/* routes redirect to the legacy account.
  • The contentSource object set to type: "CP" in your discovery.config file. See CMS for FastStore storefronts.
  • Access to the CMS Admin of your account, to publish content.
CMS-managed My Account pages aren't compatible with Headless CMS (legacy). The content type flow in cms/faststore/pages/ and the schema sync are only available in the CMS.

Creating a custom component

Before adding content to My Account pages, create a custom component following the standard FastStore CMS flow:
  1. Create the section in the src/components/sections folder and set its $componentKey.
  2. Export the section in the src/components/index.tsx file.
  3. Create the corresponding .jsonc schema in the cms/faststore/components folder.
  4. Run yarn cms-sync to make the section available in the CMS Admin.
For the complete step-by-step, see Creating a new section in the CMS.
Once your component is synced, you can place it in an existing My Account page or in a new CMS-managed page, as described in the sections below.

Choosing an approach

What you want to do?What to create?Who edits the content afterwards?
Add a section before or after the content of an existing pagebefore.tsx or after.tsx in src/myAccount/extensions/Developer (code)
Create a new page with CMS-managed contentA route with contentType in navigation.ts and a content type in the CMSMerchant (CMS Admin)
Both options use the same src/myAccount/navigation.ts file and can coexist in the same project.

Adding new sections

To add a new section before or after the content of a My Account page, follow these steps:
  1. Open your FastStore project using the code editor of your choice.
  2. Open the src/myAccount/extensions/{pageName} folder, where pageName is the URL path of the page without the /pvt/account prefix. For example:
    • For /pvt/account/orders, use src/myAccount/extensions/orders/.
    • For /pvt/account/orders/[id], use src/myAccount/extensions/orders/[id]/.
    • For a custom route such as /pvt/account/wishlist, use src/myAccount/extensions/wishlist/.
  3. In the src/myAccount/extensions/{pageName} folder, create two files: before.tsx and after.tsx. Use the before.tsx file to add a section before the existing content of the page, and use after.tsx to add a section after the existing content. Each file must export a React component as default.
For example, to add a section after the order details page, you can implement the following:
src/myAccount/extensions/orders/[id]/after.tsx

_10
function After() {
_10
return <div>After the default sections, this section is shown :)</div>;
_10
}
_10
_10
export default After;

Both default pages and custom routes created by your store can have before.tsx and after.tsx extension sections.
Based on the example above, the new section will be placed after the current content on the order details page:
{"base64":"  ","img":{"width":1999,"height":1378,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":226211,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/new-section___1ff45ac89fb0c8629c705bdc6b5deb36.png"}}

Creating a CMS-managed page

Use this approach when the merchant needs to build and edit the page content directly in the CMS Admin, without requiring a new deploy.
In this case, you don't create a .tsx file. FastStore generates the page automatically, and its whole body is composed of the sections published in the CMS.

Step 1 - Binding the route to a content type

In the src/myAccount/navigation.ts file, add the contentType property to the route. Its value must be the exact identifier of the content type you'll define in the CMS:

_11
import { getMyAccountRoutes } from "@faststore/core";
_11
_11
export default getMyAccountRoutes({
_11
routes: [
_11
{
_11
route: "/pvt/account/wishlist",
_11
title: "Wishlist",
_11
contentType: "myAccountWishlist",
_11
},
_11
],
_11
});

The contentType property is the only opt-in for CMS-managed content. Routes without it render no page body. The title property remains required, as it's the label displayed in the My Account menu. Route entries missing route or title are skipped with a warning at build time.
Declare routes as static object literals. The navigation.ts file is read through static analysis at build time, so routes built dynamically using variables, map, spread operators, or a function that returns the list aren't recognized by the page generator.
CMS-managed pages inherit the same protections as the default My Account pages, with no extra configuration: unauthenticated users are redirected to /login, sessions that require a refresh are redirected to /pvt/account/403?from=<route>, and locale validation is applied, so localized content works as expected.

Step 2 - Defining the content type in the CMS

Create the content type file in the cms/faststore/pages folder of your project. The root key must match the contentType value declared in the route:
cms/faststore/pages/cms_content_type__myaccountwishlist.jsonc

_18
{
_18
"myAccountWishlist": {
_18
"$extends": ["#/components/base-page-template"],
_18
"$singleton": true,
_18
"type": "object",
_18
"title": "My Account — Wishlist",
_18
"properties": {
_18
"sections": {
_18
"$ref": "#/$defs/$ALLOW_ALL_COMPONENTS"
_18
}
_18
},
_18
"readOnly": false,
_18
"writeOnly": false,
_18
"deprecated": false,
_18
"$abstract": false,
_18
"identifierKeys": []
_18
}
_18
}

  • $singleton: true and identifierKeys: [] make the page a single instance, without replication per entity.
  • $ALLOW_ALL_COMPONENTS in the sections property allows the merchant to use any section available in your store when building the page. To restrict the available sections, replace it with an explicit anyOf list.

Step 3 - Syncing the schema

Run the following command to send the new content type to the CMS:

_10
yarn cms-sync

For more details about the sync flow, see Local setup and development.

Step 4 - Publishing the content in the CMS Admin

  1. In the VTEX Admin, go to Storefront > Content > All content.
  2. Open the content type you created, for example, My Account — Wishlist.
  3. Add and configure the desired sections.
  4. Save and publish your changes.

Step 5 - Checking the new page

Log in to your store and access the new route, for example, /pvt/account/wishlist. The page appears in the My Account menu with the sections published in the CMS.
If the content type was never published or its identifier doesn't match the contentType value, the page still loads with the menu, layout, and authentication, but with an empty body. See Common issues during extensions section.

Rendering order

When a route has more than one content source, FastStore renders them in the following order:

_10
My Account layout
_10
└─ before.tsx
_10
└─ CMS sections
_10
└─ after.tsx

Every layer is optional. FastStore renders only the ones available for that route.

Common issues during extensions

IssuePossible cause and solution
The page loads with the menu and layout, but the body is empty.The content type was never published in the CMS Admin, or its identifier doesn't match the contentType declared in the route. Check that the root key of the .jsonc file, the contentType value, and the published content type are identical, then run yarn cms-sync again.
The build shows the Skipping CMS route <route>: only /pvt/account prefixes are allowed warning.The declared route is outside the private namespace. Move it to /pvt/account or one of its descendants.
The build shows the Skipping CMS route <route>: native page takes precedence warning.The route conflicts with a default My Account page. Native pages are always preserved. Use another path, or extend the existing page with before.tsx and after.tsx, as described in Adding new sections.
The build shows the [my-account-cms] Could not statically parse routes in <file> warning.The routes in navigation.ts aren't static object literals. Replace dynamic logic, such as variables, map, or spread operators, with literal objects.
The build shows the [my-account-cms] Skipping CMS route entry missing route/title in <file> warning.The route entry doesn't have the route or title property. Both are required.