> This page is for Developer.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bigcommerce.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bigcommerce.com/_mcp/server.

# Lab - Add Header Links

Plan: B2B Developer

Lesson 6 of 25 · 30 min

## Introduction

Let's take the first steps toward enhancing the B2B-enabled storefront with the mini-app you previously set up to integrate with the Buyer Portal.

### In this lab, you will:

* Add custom Buyer Portal links to the storefront header
* Add role-specific checks for whether to render a link

## Setup

This exercise continues where you left off previously. Make sure the dev server is started in your mini-app project:

```shell showLineNumbers={false}
npm run dev
```

> **Info**
>
> **Remember!**
>
> Script Manager in your store control panel must contain the appropriate script to load the mini-app from your local environment on the appropriate port.
>
> Revisit the initial setup lab for full details on setting up your environment.

### Exercise Reference Copy

If it's helpful to compare your own code as you go, you can clone or download the completed version of this labs as a reference.

[Header Links Lab Snapshot](https://github.com/bigcommerce-edu/lab-b2b-mini-app/tree/e-links-post)

## Injecting React components

Our mini-app is being injected via the entry point script `main.tsx`. For our initial test implementation, a typical pattern is being used to create an HTML element and `render` a root React component into it:

**`main.tsx`**

```tsx showLineNumbers={false} title="main.tsx"
const b2bRoot = document.createElement('div');
body.appendChild(b2bRoot);

createRoot(b2bRoot).render(
  <StrictMode>
    <TestApp />
  </StrictMode>,
);
```

As we flesh the app out with real content, we'll essentially be doing the same thing at multiple entry points in the DOM. Our app doesn't have a central "App" root but will `render` a root React component into each DOM location where we want to inject our content.

The lab boilerplate code includes the `createRootElement` utility function to simplify the injection of the root elements. This relies on configuration in `src/utils/dom/config.ts`:

**`config.ts`**

```ts showLineNumbers={false} title="config.ts"
export const componentDomData: Record<string, ComponentDomEntry> = {
  headerLinks: {
    selector: 'ul.navUser-section',
    rel: 'child',
    position: 'before',
    element: 'li',
    className: 'navUser-item',
  },
  ...
};
```

For unique keys corresponding with the components we'll be building, this config specifies a DOM selector and how the injected element is related to it, as well as the type and classname of the injected root element.

For the `HeaderLinks` component we'll be building in this lab, this config specifies that its root element should be injected as a child of the `ul.navUser-section` element, before any existing children, and that the root element should be an `li` element with the classname `navUser-item`.

> **Info**
>
> This configuration assumes the BigCommerce default Cornerstone theme for Stencil. Feel free to adjust this config if the DOM in your theme is different.

## Step 1: Inject Header Links

An empty `HeaderLinks` component already exists in the boilerplate code. Let's start by injecting it in `main.tsx`.

1. **Open** `main.tsx` and **remove** the existing test implementation.

**`main.tsx`**

```tsx showLineNumbers={false} title="main.tsx"
/* Remove `TestApp`
const TestApp = () => {
  const b2b = useB2B();
  return null;
}

const body = document.querySelector('body');
if (body) {
  const b2bRoot = document.createElement('div');
  body.appendChild(b2bRoot);

  createRoot(b2bRoot).render(
    <StrictMode>
      <TestApp />
    </StrictMode>,
  );
}
*/
```

2. **Add** appropriate imports and the basic component injection.

**`main.tsx`**

```tsx showLineNumbers={false} title="main.tsx"
import HeaderLinks from './components/HeaderLinks';
import { createRootElement } from './utils/dom';

const createHeaderLinks = () => {
  const root = createRootElement('headerLinks');

  if (root) {
    createRoot(root).render(
      <StrictMode>
        <HeaderLinks />
      </StrictMode>,
    );
  }
};

createHeaderLinks();
```

> **Info**
>
> Remember that the selector this injection is based on assumes the BigCommerce default Cornerstone theme for Stencil.

**Navigate to your B2B storefront. While nothing will be rendered on the page yet, the rendering of the React root is working correctly if you see the error "Uncaught Error: HeaderLinks component not implemented" in your browser developer tools console.**

> **Info**
>
> [**View the Code**](https://github.com/bigcommerce-edu/lab-b2b-mini-app/compare/e-links-01-pre...e-links-01-post?diff=split)

## Step 2: Implement the Links

Our `HeaderLinks` component will be making use of the `window.b2b.utils.openPage` method. We need to update the interface used in our hook, so that TypeScript understands the details of `openPage`.

1. **Open** `src/hooks/useB2B.ts` and **update** the definition of `B2BSdk`.

**`useB2B.ts`**

```ts {3} showLineNumbers={false} title="useB2B.ts"
export interface B2BSdk {
  utils: {
    openPage: (id: string) => void;
    user?: B2BUser;
  }
}
```

2. **Open** `src/components/HeaderLinks/index.tsx`, **remove** the error message, and **add** the `useB2B` hook.

**`HeaderLinks/index.tsx`**

```tsx {2-4, 6-7} showLineNumbers={false} title="HeaderLinks/index.tsx"
function HeaderLinks() {
  /* Remove this
  throw new Error('HeaderLinks component not implemented');
  */

  const b2b = useB2B();
  if (!b2b) return null;

  
  return (
     b2b && (
    <>

    </>
  );
  )
}
```

Remember, the `useB2B` hook returns the `window.b2b` object *as soon as it's available*. When this occurs, the updated state value means `HeaderLinks` will re-render.

3. **Add** the `button` elements for the links.

**`HeaderLinks/index.tsx`**

```tsx {5-8} showLineNumbers={false} title="HeaderLinks/index.tsx"
function HeaderLinks() {
  ...
 
  return (
  b2b && (
    <>
      <button className="b2b-nav" onClick={() => b2b.utils.openPage('COMPANY_ORDERS')}>Orders</button>
      <button className="b2b-nav" onClick={() => b2b.utils.openPage('INVOICE')}>Invoices</button>
    </>
  );
  )
}
```

**Navigate to your B2B storefront. You should now see the "Orders" and "Invoices" links in the header.**

![Header links](/_fern-img/727d907ad8f7a0bd650d483bf46e3f4fad08857727775094253569f7962c75f2.webp)

**Make sure you're logged in as a company user and try out the links. The Buyer Portal should open to the appropriate page.**

> **Info**
>
> [**View the Code**](https://github.com/bigcommerce-edu/lab-b2b-mini-app/compare/e-links-02-pre...e-links-02-post?diff=split)

## Step 3: Add Role Checks

Currently, the custom links render on the page regardless of the user's permissions, or even whether they're logged in at all. Let's add an appropriate inspection of the current user's role.

Once again, we're making use of a new method from `b2b.utils` (the `getProfile` method of `b2b.utils.user`) and need to update the appropriate interface.

1. **Open** `src/hooks/useB2B.ts` and **update** the definition of `B2BUser`.

**`useB2B.ts`**

```ts {2-4} showLineNumbers={false} title="useB2B.ts"
export interface B2BUser {
  getProfile: () => {
    role: number;
  }
}
```

We'll be using the current user's role to determine whether they should see the "Orders" and "Invoices" links. Unfortunately, we don't have a way of retrieving the enumeration values that correspond with different roles, so we'll define these manually in our app. (These values can be found in the Buyer Portal source code.)

We'll allow users with Admin and Senior Buyer roles to see the links, as well as a few special roles for which we're not diving into the details here.

2. **Open** `src/components/HeaderLinks/index.tsx` and **add** `allowedRoles`.

**`HeaderLinks/index.tsx`**

```tsx {3-10} showLineNumbers={false} title="HeaderLinks/index.tsx"
import './HeaderLinks.css';

// Define role codes that are allowed to see the header links
const allowedRoles = [
  0, // ADMIN
  1, // SENIOR_BUYER
  3, // SUPER_ADMIN
  4, // SUPER_ADMIN_BEFORE_AGENCY
  5, // CUSTOM_ROLE
];

function HeaderLinks() {
  ...
}
```

3. **Add** a state value to track whether the user has the appropriate role, as well as a side effect to check the user's role when `b2b` is available.

**`HeaderLinks/index.tsx`**

```tsx {2, 6-8, 10-18} showLineNumbers={false} title="HeaderLinks/index.tsx"
function HeaderLinks() {
  const [userIsValid, setUserIsValid] = useState(false);

  const b2b = useB2B();

  /* Remove this
  if (!b2b) return null;
  */

  useEffect(() => {
    if (!b2b) return;

    // Get the B2B user profile
    const userProfile = b2b.utils.user ? b2b.utils.user.getProfile() : null;
    if (userProfile && allowedRoles.includes(userProfile.role)) {
      setUserIsValid(true);
    }
  }, [b2b]);

  return (
    ...
  );
}
```

Note that we no longer need the original `if (!b2b)` check after `useB2B`, since the side effect will now handle this.

4. **Add** a conditional to the JSX.

**`HeaderLinks/index.tsx`**

```tsx {5, 10} showLineNumbers={false} title="HeaderLinks/index.tsx"
function HeaderLinks() {
  ...

  return (
    userIsValid && b2b && (
    <>
      <button ...>Orders</button>
      <button ...>Invoices</button>
    </>
    )
  );
}
```

**Now try alternating between logging in as a user with the Junior Buyer role and a user with a more elevated role. The rendering of the links should only occur for users with the appropriate role.**

> **Info**
>
> [**View the Code**](https://github.com/bigcommerce-edu/lab-b2b-mini-app/compare/e-links-03-pre...e-links-03-post?diff=split)

## Full Exercise Code

> **Info**
>
> [**Full Exercise Code**](https://github.com/bigcommerce-edu/lab-b2b-mini-app/compare/e-links-pre...e-links-post?diff=split)

## Taking It Further

Practice the end-to-end process of injecting a new component at a different entry point and integrating that component with B2B data. This will involve updating `src/utils/dom/config.ts` and `main.tsx` in addition to the details of your new component. Don't forget to update the `B2BSdk` interface for any new B2B object data you're interacting with. As a starter idea for data that could be incorporated into a global component, consider that `b2b.utils.quote` contains the method `getCurrent`, which will return the details of the B2B quote in progress.