FastComments.com

Add Comments to GoHighLevel Sites

With FastComments we can easily add live commenting to any site built with GoHighLevel.

Note that this tutorial requires a FastComments account. It's recommended that you sign up first and then come back here. You can create an account here.

Signing in first will ensure the generated code snippets are already tied to your account.

GoHighLevel Membership Sites and Other Sites

This tutorial is split between two categories: Membership Sites and regular GoHighLevel Sites.

We start with the instructions for Membership Sites.

Step 1: Edit Course Internal Link

First, we're going to edit the settings for our course.

To do this, open the course, and click Edit Details.

Edit Course Details
Edit Course Details

Step 2: Open Advanced Settings Internal Link

Next, we need to open the Advanced settings:

Open Advanced Settings
Open Advanced Settings

We'll be adding our code to the Tracking Code section. Go to that section and click Footer Code.

Step 3: Copy Code Internal Link

Now we're going to copy our FastComments code. Try using the "Copy" button on the right, as the code is quite large to work properly with GoHighLevel:

GoHighLevel FastComments Code Snippet
Copy Copy
1
2<script>
3 <script>
4 (function () {
5 const tenantId = 'demo';
6 const VALID_PATTERNS = ['/post'];
7 const TYPE = 'commenting'; // set to one of: commenting, live, collab
8 const SCRIPT_ID = 'fastcomments-embed';
9 const TARGET_ELEMENT_ID = ''; // you can set this to add the widget to a specific area of the page by adding a separate HTML element with a div
10
11 function getConstructor() {
12 if (TYPE === 'commenting') {
13 return window.FastCommentsUI;
14 }
15 if (TYPE === 'live') {
16 return window.FastCommentsLiveChat;
17 }
18 if (TYPE === 'collab') {
19 return window.FastCommentsCollabChat;
20 }
21 }
22
23 function getScript() {
24 if (TYPE === 'commenting') {
25 return 'https://cdn.fastcomments.com/js/embed-v2.min.js';
26 }
27 if (TYPE === 'live') {
28 return 'https://cdn.fastcomments.com/js/embed-live-chat.min.js';
29 }
30 if (TYPE === 'collab') {
31 return 'https://cdn.fastcomments.com/js/embed-collab-chat.min.js';
32 }
33 }
34
35 // Function to ensure script is loaded
36 function ensureScriptLoaded() {
37 return new Promise((resolve) => {
38 // Check if script tag already exists
39 let scriptTag = document.getElementById(SCRIPT_ID);
40
41 if (!scriptTag) {
42 console.log('FastComments: Script tag not found, adding dynamically...');
43 scriptTag = document.createElement('script');
44 scriptTag.id = SCRIPT_ID;
45 scriptTag.src = getScript();
46 scriptTag.async = true;
47
48 scriptTag.onload = () => {
49 console.log('FastComments: Script loaded successfully');
50 resolve();
51 };
52
53 scriptTag.onerror = () => {
54 console.error('FastComments: Failed to load script');
55 resolve(); // Resolve anyway to prevent hanging
56 };
57
58 document.head.appendChild(scriptTag);
59 } else if (getConstructor()) {
60 // Script tag exists and is already loaded
61 console.log('FastComments: Script already loaded');
62 resolve();
63 } else {
64 // Script tag exists but not ready yet
65 console.log('FastComments: Waiting for script to initialize...');
66 scriptTag.addEventListener('load', () => {
67 resolve();
68 });
69
70 // Fallback in case the script is already loading
71 const checkInterval = setInterval(() => {
72 if (getConstructor()) {
73 clearInterval(checkInterval);
74 resolve();
75 }
76 }, 100);
77
78 // Timeout after 10 seconds
79 setTimeout(() => {
80 clearInterval(checkInterval);
81 console.warn('FastComments: Script load timeout');
82 resolve();
83 }, 10000);
84 }
85 });
86 }
87
88 // History API modifications for SPA support
89 const oldPushState = history.pushState;
90 history.pushState = function pushState() {
91 const ret = oldPushState.apply(this, arguments);
92 window.dispatchEvent(new Event('pushstate'));
93 window.dispatchEvent(new Event('locationchange'));
94 return ret;
95 };
96
97 const oldReplaceState = history.replaceState;
98 history.replaceState = function replaceState() {
99 const ret = oldReplaceState.apply(this, arguments);
100 window.dispatchEvent(new Event('replacestate'));
101 window.dispatchEvent(new Event('locationchange'));
102 return ret;
103 };
104
105 window.addEventListener('popstate', () => {
106 window.dispatchEvent(new Event('locationchange'));
107 });
108
109 let lastInstance;
110 let currentUrlId;
111
112 // Main render function
113 async function render() {
114 let rendered = false;
115
116 // Ensure script is loaded before proceeding
117 await ensureScriptLoaded();
118
119 function tryNext() {
120 if (rendered) {
121 return;
122 }
123
124 // Check if we should render on this page
125 if (!VALID_PATTERNS.some(function (pattern) {
126 return window.location.pathname.includes(pattern);
127 })) {
128 console.log('FastComments: Not set to load on this page. Waiting.');
129 setTimeout(tryNext, 1000);
130 return;
131 }
132
133 // Double-check if available
134 if (!getConstructor()) {
135 console.log('FastComments: not ready, waiting...');
136 setTimeout(tryNext, 300);
137 return;
138 }
139
140 function getContainer() {
141 let container;
142 if (TARGET_ELEMENT_ID) {
143 container = document.getElementById(TARGET_ELEMENT_ID);
144 } else {
145 // Try to find container with multiple selectors
146 container = document.querySelector('#post-body');
147 if (!container) {
148 container = document.querySelector('#content-container #content-container #post-description');
149 }
150 if (!container) {
151 container = document.querySelector('#post-description');
152 }
153 if (!container) {
154 container = document.querySelector('#content-container');
155 }
156 if (!container) {
157 container = document.querySelector('.post-description'); // mobile
158 }
159 }
160 return container;
161 }
162
163 // Look for the target element with specific ID
164 let container = getContainer();
165
166 if (container) {
167 console.log('FastComments: Target element found, initializing...');
168
169 // Get urlId attribute
170 const urlIdAttr = container.getAttribute('urlId');
171
172 // Check if we need to re-render (urlId changed or first render)
173 if (currentUrlId !== urlIdAttr || !lastInstance) {
174 currentUrlId = urlIdAttr;
175
176 // Destroy previous instance if exists
177 if (lastInstance) {
178 lastInstance.destroy();
179 // Clear the container content
180 container.innerHTML = '';
181 }
182
183 // Prepare config
184 const config = {
185 tenantId,
186 showLiveRightAway: true
187 };
188
189 // Only add urlId to config if it's not "auto"
190 if (urlIdAttr && urlIdAttr !== 'auto') {
191 config.urlId = urlIdAttr;
192 console.log('FastComments: Using urlId:', urlIdAttr);
193 } else {
194 console.log('FastComments: Using auto URL determination');
195 }
196
197 // Initialize FastComments
198 lastInstance = getConstructor()(container, config);
199 rendered = true;
200 } else {
201 console.log('FastComments: Already rendered with same urlId');
202 rendered = true;
203 }
204
205 // Monitor if container gets removed or urlId changes
206 const interval = setInterval(function () {
207 const currentContainer = getContainer();
208 if (!currentContainer) {
209 console.log('FastComments: Container removed, will retry...');
210 rendered = false;
211 currentUrlId = null;
212 tryNext();
213 clearInterval(interval);
214 } else {
215 const newUrlId = currentContainer.getAttribute('urlId');
216 if (newUrlId !== currentUrlId) {
217 console.log('FastComments: urlId changed, re-rendering...');
218 rendered = false;
219 tryNext();
220 clearInterval(interval);
221 }
222 }
223 }, 1000);
224 } else {
225 console.log('FastComments: Target element not found, waiting...');
226 setTimeout(tryNext, 300);
227 }
228 }
229
230 tryNext();
231 }
232
233 // Initial render
234 render();
235
236 // Re-render on location change
237 window.addEventListener('locationchange', function () {
238 console.log('FastComments: Location changed, updating...');
239 render();
240 });
241 })();
242</script>
243

You can configure the VALID_PATTERNS variable to set which URL routes the comments should show. By default, it will show on pages that contain /post in the URL.

You can configure the TYPE = 'commenting' line to switch the product used (for example you can change it to streaming for streaming chat or collab for collab chat).

Step 4: Paste Code Internal Link

Now that we've copied our snippet, paste it in the Footer Code section as shown:

Paste Code
Paste Code

Member Site Success Internal Link

That's it! You should now have live commenting added to your GoHighLevel course.

Success
Success

If you've run into a permission denied error, or would like to customize FastComments, read on.

Member Site Customization Internal Link

FastComments is designed to be customized to match your site.

If you'd like to add custom styling, or tweak configuration, Checkout our Customization Documentation to learn how.

Step 1: Add Custom Code Element Internal Link

First, we're going to open the editor for the page of our site we want to add comments to.

Open Editor
Open Editor

Now find the place on the page where you want to add comments. Move your mouse toward the end of that area. A + icon will appear:

Add Section
Add Section

If we click that it asks us how many columns should the new section be. We'll select 1 COLUMN:

Add a Column
Add a Column

Now if you move your mouse over the new 1-column-row you'll have the option to add an element. Click that:

Add Element
Add Element

Scroll down and pick CUSTOM JS/HTML:

Select CUSTOM JS/HTML
Select CUSTOM JS/HTML

Now select our new element and click Open Code Editor on the left:

Open Code Editor
Open Code Editor

Step 2: Copy and Paste Code Internal Link

It's time to copy our code. Copy the following code:

GoHighLevel Site Comments Code
Copy Copy
1
2<script src="https://cdn.fastcomments.com/js/embed-v2.min.js"></script>
3<div id="fastcomments-widget"></div>
4<script>
5 FastCommentsUI(document.getElementById('fastcomments-widget'), {
6 tenantId: "demo",
7 urlId: window.location.pathname
8 });
9</script>
10

Paste that in the editor window we opened:

Paste Code
Paste Code

We can now click Yes, Save on the bottom right of that window.

At the top of the page now click Save and then Preview.

Site Success Internal Link

That's it! You should now have live commenting added to your GoHighLevel site.

Success
Success

If you've run into a permission denied error, or would like to customize FastComments, read on.

Site Customization Internal Link

FastComments is designed to be customized to match your site.

If you'd like to add custom styling, or tweak configuration, Checkout our Customization Documentation to learn how.

In Conclusion

If for any reason the provided steps or code is not working, please let us know.