Make help part of your own interface: open the widget from your own button, let links in your app open articles inside the widget, and show the articles that fit the page a visitor is on.
You need: the widget on your page (see Widget installation and options) · Plans: All plans
Open the widget from your own button
Hide the launcher with showButton: false, and open the widget from your button with hcWidget.show(). The button starts disabled and turns on when the widget is ready, so an early click is never lost:
<button type="button" id="help-button" disabled>Help</button>
<script>
window.hcOptions = {
app_id: 'YOUR_WIDGET_ID',
showButton: false,
onWidgetReady: function () {
document.getElementById('help-button').disabled = false;
}
};
document.getElementById('help-button').addEventListener('click', function () {
window.hcWidget.show();
});
</script>
<script src="https://helpcenter.io/js/init.js" async></script>
With the launcher hidden, the widget opens as a full-height panel, 460 pixels wide, at the right edge of the page (the left edge with position: 'bottom-left'), and full screen on screens narrower than 450 pixels. Visitors close it with its close button; your code can close it with hcWidget.hide(). Proactive messages don't show while the launcher is hidden.
You can also keep the launcher and add your own button next to it: show() works either way.
Make links open articles in the widget
Add a data-hc-article attribute to a link to one of your articles. A click then opens the article inside the widget instead of leaving the page:
<p>
Forgot your password?
<a href="https://help.example.com/content/reset-your-password" data-hc-article>Reset it</a>
</p>
What makes a link work:
The address contains
/content/and the article's slug in your default language. Whatever comes before/content/doesn't matter. A link to a translated address, such as/es/content/restablecer-tu-contrasena, opens the widget without the article.The link has
data-hc-article. Any value, or none, works.The link is on the page when
init.jsruns. Links added later behave as ordinary links.The click lands on the link itself. A click on an icon, image or other element inside the link does nothing. Keep these links text only, or use the handler below.
Timing matters too. Before init.js runs, the link is an ordinary link. From then until the widget is ready, a click does nothing, and on a page whose host isn't in Embedding origins the widget is never ready. A Ctrl-click or Cmd-click also opens the article in the widget, not in a new tab.
Links added later, and icon links
For links your app renders after the page loads, and links that contain icons, handle the clicks yourself with hcWidget.setHrefAndOpen(). Mark these links with an attribute of your own, such as data-help-article, so the widget's built-in handling stays out of the way:
// Opens links marked data-help-article in the widget, wherever and whenever they
// appear. Modified clicks, and clicks before the widget is ready, behave as normal links.
document.addEventListener('click', function (event) {
var link = event.target.closest('a[data-help-article]');
if (!link || event.defaultPrevented || event.button !== 0 ||
event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return;
}
if (window.hcWidget && window.hcWidget.isReady()) {
event.preventDefault();
window.hcWidget.setHrefAndOpen(link.href);
}
});
The listener sits on the whole document, so it covers links added at any time and clicks anywhere inside a link. A Ctrl-click or Cmd-click opens the article in a new tab, and until the widget is ready the link takes the visitor to your help center.
<a href="https://help.example.com/content/how-billing-works" data-help-article>
<svg width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7"></circle></svg>
How billing works
</a>
Show one category's articles
On your billing page, the widget can list your billing articles instead of your most viewed ones. Set the category in your options, and change it with hcWidget.setCategory() when your app changes pages without a reload:
window.hcOptions = {
app_id: 'YOUR_WIDGET_ID',
category: 4821
};
// Your app's sections, and the help center category that matches each one.
var helpCategories = { '/billing': 4821, '/account': 4822 };
function showHelpFor(path) {
var categoryId = helpCategories[path];
if (categoryId && window.hcWidget) {
window.hcWidget.onReady(function () {
window.hcWidget.setCategory(categoryId);
});
}
}
Call showHelpFor(location.pathname) from your router after every route change. What visitors see:
The home screen lists up to seven published articles from the category and its subcategories.
With
categoryin your options, the Can this help? suggestions for the page are off.Use a category every visitor can see. With the option, a category that's hidden from visitors gives an empty list. With
setCategory, it gives your most viewed articles.setCategorydoesn't open the widget, and no call returns the list to your most viewed articles; reload the page for that.
Find a category's ID
Open the category on your help center. The ID is the number after /category/ in its address: in https://help.example.com/category/4821-billing, or /en/category/4821-billing on a help center with several languages, it's 4821.
To map many categories, list them with the API. A key with read access is enough:
curl -sS "https://api.helpcenter.io/v1/categories" \
-H "apikey: $HELPCENTER_API_KEY" \
-H "Accept: application/json"
Each category has its id, name and privacy. Pick categories whose privacy is public, under parent categories that are public too: a category under a hidden parent is hidden as well. The response, shortened to two categories:
{
"status": "success",
"categories": [
{
"id": 314,
"parent": null,
"name": { "en": "Billing" },
"description": [],
"icon": null,
"position": 100000,
"privacy": "public",
"created_at": "2026-09-30 08:19:35",
"updated_at": "2026-09-30 08:19:35"
},
{
"id": 319,
"parent": null,
"name": { "en": "Internal" },
"description": [],
"icon": null,
"position": 100000,
"privacy": "team_private",
"created_at": "2026-09-30 08:20:27",
"updated_at": "2026-09-30 08:20:27"
}
],
"meta": { "page": 1, "per_page": 100, "total_pages": 1, "items_count": 4 }
}
More about the endpoint is in Categories.