GHL Experts

How to Add Custom Buttons to the GoHighLevel Membership Sidebar (Classic Theme)

Add custom call-to-action buttons to your GoHighLevel membership site sidebar using this JavaScript snippet. Supports fill and outline styles with full CSS customization.

December 6, 2020
6 min read

Why Add Custom Buttons to Your GHL Membership Sidebar

GoHighLevel's membership sites are solid for delivering courses and gated content, but the default sidebar in the Classic Theme is limited. It shows the instructor card and navigation, and that is about it. There is no built-in way to add call-to-action buttons, promotional links, or navigation shortcuts to the sidebar area.

This is a missed opportunity. The sidebar is visible on every category page, which makes it prime real estate for driving members toward specific actions: upgrading to a higher tier, joining an affiliate program, accessing help resources, or visiting an external page.

The solution is a JavaScript snippet that dynamically injects custom buttons into the sidebar. You configure the buttons in a simple array, choose between fill and outline styles, and the script handles the rest -- including element detection, insertion, and page-change handling.

This approach works with the Classic membership theme in GoHighLevel. It targets the categories view and inserts buttons above the instructor card in the sidebar.

The Complete Code

Here is the full script. You will paste this into your membership site's Custom JS field.

1// ****** Sidebar button start ********
2(function () {
3 const buttons = [
4 {
5 name: "👉🏻 Upgrade To Next Level 👈🏻",
6 id: "btn-1",
7 actionURL: "https://www.theghlacademy.com/upgrade",
8 style: "fill",
9 },
10 {
11 name: "Become An Affiliate",
12 id: "btn-2",
13 actionURL: "https://www.theghlacademy.com/affiliate",
14 style: "fill",
15 },
16 {
17 name: "View Help Resources",
18 id: "btn-3",
19 actionURL: "https://www.theghlacademy.com/help",
20 style: "fill",
21 },
22 ];
23
24 const customButtonsStyleSheet = `
25 :root {
26 --cm-buttons-font-family: 'Roboto', sans-serif;
27 --custom-cm-btn-border-radius: 5px;
28 --custom-cm-btn-padding: 10px 25px;
29 --custom-cm-btn-font-size: 16px;
30 --custom-cm-btn-font-weight: 700;
31 --custom-cm-btn-background-color: rgb(33, 46, 75);
32 --custom-cm-btn-font-color: #fff;
33 --custom-cm-outline-btn-background-color: transparent;
34 --custom-cm-outline-btn-font-color: rgb(24, 15, 120);
35 --custom-cm-btn-outline-border: 1px solid rgb(24, 15, 120);
36 --transition-all: all 0.3s ease-in;
37 }
38
39 .cm-buttons-con {
40 margin-bottom: 20px !important;
41 display: flex !important;
42 align-items: stretch !important;
43 justify-content: stretch !important;
44 flex-direction: column !important;
45 }
46
47 .cm-buttons-con .cm-custom-button {
48 width: 100% !important;
49 display: inline-flex !important;
50 padding: var(--custom-cm-btn-padding) !important;
51 align-items: center !important;
52 justify-content: center !important;
53 text-align: center !important;
54 font-size: var(--custom-cm-btn-font-size) !important;
55 font-weight: var(--custom-cm-btn-font-weight) !important;
56 border-radius: var(--custom-cm-btn-border-radius) !important;
57 text-decoration: none !important;
58 transition: var(--transition-all) !important;
59 }
60
61 .cm-buttons-con .cm-custom-button:not(:last-child) {
62 margin-bottom: 15px !important;
63 }
64
65 .cm-buttons-con .cm-custom-button.fill {
66 background-color: var(--custom-cm-btn-background-color) !important;
67 color: var(--custom-cm-btn-font-color) !important;
68 border: 1px solid transparent !important;
69 }
70
71 .cm-buttons-con .cm-custom-button.fill:hover {
72 background-color: var(--custom-cm-outline-btn-background-color) !important;
73 color: var(--custom-cm-outline-btn-font-color) !important;
74 border: var(--custom-cm-btn-outline-border) !important;
75 }
76
77 .cm-buttons-con .cm-custom-button.outline {
78 background-color: var(--custom-cm-outline-btn-background-color) !important;
79 color: var(--custom-cm-outline-btn-font-color) !important;
80 border: var(--custom-cm-btn-outline-border) !important;
81 }
82
83 .cm-buttons-con .cm-custom-button.outline:hover {
84 background-color: var(--custom-cm-btn-background-color) !important;
85 color: var(--custom-cm-btn-font-weight) !important;
86 }
87 `;
88
89 const head = document.querySelector("head");
90 const style = document.createElement("style");
91 style.innerHTML = customButtonsStyleSheet;
92 head.append(style);
93
94 const customBtnsCon = document.createElement("div");
95 customBtnsCon.className = "cm-buttons-con";
96
97 buttons.forEach((button) => {
98 const btn = document.createElement("button");
99 const buttonClass = `cm-custom-button ${button.name.toLowerCase()} ${button.style}`;
100 btn.className = buttonClass;
101 btn.innerHTML = button.name;
102 btn.id = button.id;
103 btn.addEventListener("click", () => {
104 window.open(button.actionURL, "_blank");
105 });
106 customBtnsCon.appendChild(btn);
107 });
108
109 const getElementByFn = (selector, cb) => {
110 const intervalId = setInterval(() => {
111 const element = document.querySelectorAll(selector);
112 if (element.length === 1) {
113 clearInterval(intervalId);
114 cb(element[0]);
115 }
116 if (element.length > 1) {
117 clearInterval(intervalId);
118 cb(element);
119 }
120 }, 200);
121 setTimeout(function () {
122 clearInterval(intervalId);
123 }, 20000);
124 };
125
126 const runCustomCode = (pathname) => {
127 if (!pathname.includes("categories")) return;
128 if (customBtnsCon.isConnected) customBtnsCon.remove();
129 getElementByFn("#instructor-card", (lessonCard) => {
130 const sidebar = lessonCard.parentElement;
131 if (!sidebar) return console.log("Button Not inserted is because ref element not found");
132 sidebar.insertBefore(customBtnsCon, sidebar.childNodes[0]);
133 });
134 };
135
136 let pathname = "";
137 window.addEventListener("DOMNodeInserted", (e) => {
138 if (pathname == location.pathname) return;
139 pathname = location.pathname;
140 runCustomCode(pathname);
141 });
142})();
143// ****** Sidebar button End ********

Where to Install the Script

1. Open your GoHighLevel sub-account.

2. Navigate to Memberships and select the membership site you want to customize.

3. Go to Settings (gear icon).

4. Click the Advanced tab.

5. Paste the entire script into the Custom JS field.

6. Save your changes.

The script will load on every page of your membership site but only activates on category pages, where the sidebar with the instructor card is present.

How the Script Works

The script follows a straightforward pattern: define buttons, inject styles, create DOM elements, and insert them into the sidebar when the right page loads.

Button Configuration

The buttons array at the top of the script is where you define your buttons. Each button object has four properties:

| Property | Description | Example |

|----------|-------------|---------|

| name | The text displayed on the button | "Become An Affiliate" |

| id | A unique HTML id for the button element | "btn-2" |

| actionURL | The URL the button opens (in a new tab) | "https://example.com/affiliate" |

| style | Visual style: "fill" or "outline" | "fill" |

You can add as many buttons as you need. Just add another object to the array:

1const buttons = [
2 {
3 name: "Upgrade Your Plan",
4 id: "btn-1",
5 actionURL: "https://yoursite.com/upgrade",
6 style: "fill",
7 },
8 {
9 name: "Join Our Community",
10 id: "btn-2",
11 actionURL: "https://yoursite.com/community",
12 style: "outline",
13 },
14];

Button Styles

The script supports two visual styles:

Fill style renders a solid button with a colored background and white text. On hover, it inverts to a transparent background with a colored border and colored text.

Outline style renders a transparent button with a colored border and colored text. On hover, it fills in with the solid background color.

This gives you a clean way to create visual hierarchy. Use fill for your primary action and outline for secondary links.

Element Detection

GHL membership pages load content dynamically, which means the sidebar elements may not exist in the DOM when the script first runs. The getElementByFn function handles this by polling the DOM every 200 milliseconds for the target selector. Once the element appears, it calls the callback and clears the interval.

There is a 20-second timeout to prevent the interval from running indefinitely if the element never appears.

Page Change Detection

The script listens for DOMNodeInserted events to detect when the user navigates to a different page within the membership site. Since GHL memberships use client-side routing, traditional page load events do not fire. When the pathname changes, the script checks if the new page is a categories page and re-inserts the buttons if needed.

Customizing the CSS Variables

The stylesheet uses CSS custom properties so you can adjust the appearance without modifying the button logic. Here is what each variable controls:

| Variable | Default | What It Controls |

|----------|---------|-----------------|

| --cm-buttons-font-family | 'Roboto', sans-serif | Font used for button text |

| --custom-cm-btn-border-radius | 5px | Corner rounding on buttons |

| --custom-cm-btn-padding | 10px 25px | Internal spacing of buttons |

| --custom-cm-btn-font-size | 16px | Button text size |

| --custom-cm-btn-font-weight | 700 | Button text weight (boldness) |

| --custom-cm-btn-background-color | rgb(33, 46, 75) | Fill button background color |

| --custom-cm-btn-font-color | #fff | Fill button text color |

| --custom-cm-outline-btn-background-color | transparent | Outline button background |

| --custom-cm-outline-btn-font-color | rgb(24, 15, 120) | Outline button text color |

| --custom-cm-btn-outline-border | 1px solid rgb(24, 15, 120) | Outline button border |

| --transition-all | all 0.3s ease-in | Hover transition speed and easing |

To match your brand colors, update the --custom-cm-btn-background-color and related variables. For example, to use a blue and white scheme:

1--custom-cm-btn-background-color: #2563eb;
2--custom-cm-btn-font-color: #ffffff;
3--custom-cm-outline-btn-font-color: #2563eb;
4--custom-cm-btn-outline-border: 1px solid #2563eb;

Common Use Cases

Upsell and Upgrade Links

If you run a tiered membership, the sidebar is an ideal spot for an upgrade CTA. Members see it on every category page, which creates consistent visibility without interrupting the learning experience.

1{
2 name: "Unlock Premium Content",
3 id: "btn-upgrade",
4 actionURL: "https://yoursite.com/upgrade",
5 style: "fill",
6}

Affiliate Program Promotion

For membership operators running an affiliate program, a sidebar button is a low-friction way to drive sign-ups. Members who are actively engaged with your content are your best candidates for affiliates.

Help and Support Links

Adding a help resources button reduces support tickets by giving members a visible path to self-service documentation, FAQs, or a knowledge base.

External Community Links

If you maintain a community on Facebook Groups, Skool, Circle, or another platform, a sidebar button can bridge the gap between your course content and your community space.

Adapting the Script for Different Pages

By default, the script only runs on pages whose pathname includes "categories". If you want the buttons to appear on other pages, modify the condition in the runCustomCode function:

1const runCustomCode = (pathname) => {
2 // Show on all pages (remove the categories check)
3 if (customBtnsCon.isConnected) customBtnsCon.remove();
4 getElementByFn("#instructor-card", (lessonCard) => {
5 const sidebar = lessonCard.parentElement;
6 if (!sidebar) return;
7 sidebar.insertBefore(customBtnsCon, sidebar.childNodes[0]);
8 });
9};

Alternatively, you can target specific paths:

1if (!pathname.includes("categories") && !pathname.includes("lessons")) return;

Troubleshooting

Buttons do not appear: The script waits for the #instructor-card element, which is specific to the Classic Theme sidebar. If you are using a different membership theme, this selector will not match. Inspect the page to find the correct sidebar element selector for your theme.

Buttons disappear on navigation: This is expected behavior. The script re-inserts buttons when the pathname changes. If the new page does not match the pathname filter, the buttons will not be inserted. Check the runCustomCode condition.

Styling looks off: Make sure you are not conflicting with existing custom CSS on your membership site. The script uses !important declarations to override theme defaults, but other custom code with higher specificity could still interfere.

20-second timeout: If your membership pages take longer than 20 seconds to fully render the sidebar, increase the timeout value in the setTimeout call at the end of the getElementByFn function.

Limitations

This script is designed for the Classic membership theme. GoHighLevel has introduced newer membership themes with different DOM structures, so the #instructor-card selector may not work on those themes. If you are using a newer theme, you will need to inspect the page and update the selector to match the sidebar container in your theme.

The DOMNodeInserted event is deprecated in modern browsers, though it still works in all current browsers as of 2026. A more future-proof approach would be to use a MutationObserver, but the current implementation remains functional and the membership site environment is controlled enough that compatibility is not a practical concern.

Want to try HighLevel?

If you're exploring marketing automation platforms, HighLevel offers a 30-day free trial.

Start your free trial