
By going to Settings > Widget, you can easily add a widget to your site. It only takes a few lines of JavaScript to set up, providing you with a simple way to unify your support, Changelog, docs, and AI Chat for your customers. You can customize the widget’s appearance to fit your site’s design, listen to events & more.
Embed & initialize
Head to the widget settings page, where you’ll find a copy/pastable snippet that looks like the snippet below. Or copy the code below and replace <your-widget-key /> with your workspace’s widget key.
<script>
;((w)=>{const P=(w.Productlane={queue:{}});["set","open","close","toggle","on","off","init"].forEach(m=>{P[m]=(n=>function(){P.queue[n]={args:arguments}})(m)})})(window);
Productlane.init({
widgetKey: "<your-widget-key />",
})
</script>
<script
async
defer
crossorigin="anonymous"src="https://widget.productlane.com/latest.productlane-widget.min.js"
></script>That’s it!
While this is all you have to do, let’s briefly unwrap what happens in the snippet.
The first script sets up the global Productlane object used to communicate with the widget. It also initializes the widget via Productlane.init() with your corresponding widget key.
The second script tag loads the widget from Productlane’s CDN.
The widget key can be found on your widget settings page.
Productlane.init({
widgetKey: "<your-widget-key />",
});An object containing information about the current user for simple authentication. Will hide the email field in the contact form if provided.
Productlane.init({
widgetKey: "<your-widget-key />",
user: {
email: "[email protected]",
},
});To verify your users' identities and ensure conversations are securely linked to them, enable secure mode by passing a signed JWT (JSON Web Token) to the widget.
Generate the Token
You need to generate a JWT on your backend using a Signing Secret. You can find this secret in your Widget Settings.
The token must be signed using the HS256 algorithm and include the user's email, an issued-at timestamp (iat), and an expiration timestamp (exp).
The expiration timestamp can not exceed 1 hour. This does not affect the time your user is signed into the widget. After the user token is passed to the widget, it is exchanged for a session token. The session token is valid for 12 hours by default.
Here is an example using Node.js and the jsonwebtoken library:
const jwt = require('jsonwebtoken');
const secret = "wg_sec_..."
const payload = {
email: user.email,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (15 * 60) // 15 minutes (recommended)
};
const userToken = jwt.sign(payload, secret, { algorithm: 'HS256' })Pass Token to Widget
Once generated, pass the token to the widget using the userToken property. You can do this during initialization, or update it dynamically later.
On Initialization:
Productlane.init({
widgetKey: "<your-widget-key />",
userToken: "<user-token>"
});Dynamically (e.g., after login):
Productlane.set({
userToken: "<user-token>"
});To sign the user out and unset the authentication token, use the signOut method:
Productlane.signOut();Important Note on User Objects
The userToken provides secure authentication. If you subsequently set the simple user object (e.g., Productlane.set({ user: { email: ... } })), it will overwrite the authentication status and effectively sign the user out of the secure session. Ensure you use either userToken for authenticated users or user for simple identification, but avoid mixing them in the same session state.
When a user is authenticated in the widget via JWT, they are automatically signed into the portal when clicking any link that opens it. For example the roadmap, changelog, docs, or requests. This works like single sign-on (SSO): no separate login is required on the portal side.
The user authenticates in the widget using a userToken (JWT).
When they click a link to the portal (e.g., to view a roadmap item or help article), the widget requests a short-lived, one-time authorization code.
The portal exchanges this code for a session, automatically signing the user in.
To use this feature, both Identity Verification and Portal Auth must be enabled in your Widget Settings.
The widget supports dynamic context passing, which helps the AI Chat provide more relevant and page-specific answers to your users. By setting context information about the current page or feature your user is viewing, the AI can tailor its responses accordingly.
Use the setContext method to pass contextual information to the widget. Context is provided as an object with string key-value pairs.
Productlane.setContext({
page: "dashboard",
feature: "analytics",
plan: "pro"
});Common use cases:
Page tracking: Let the AI know which page the user is currently on
Feature context: Indicate which feature or section the user is interacting with
User attributes: Pass relevant user information like their plan type or role
Product state: Provide context about what the user is viewing or editing
You can retrieve the currently set context at any time:
const currentContext = Productlane.getContext();
console.log(currentContext); // { page: "dashboard", feature: "analytics" }To remove all context information:
Productlane.clearContext();When a user asks a question in the AI Chat, the context you've set is automatically included with their message. This allows the AI to:
Prioritize information relevant to the current page or feature
Provide more specific and actionable answers
Reference documentation or help articles related to what the user is currently doing
For example, if a user is on your "Analytics Dashboard" page and asks "How do I export this data?", the AI will know they're referring to analytics data specifically, not data from other parts of your application.
To align the widget with your brand guidelines, you can customize its accent colors. We support the following theme options:
Productlane.init({
widgetKey: "<your-widget-key />",
theme: {
"--accent-color": "#1692A4",
"--accent-color-dark": "#1692A4",
},
});
The widget will automatically apply either the regular or -dark variant based on the user’s system preference.
You can also explicitly set the widget's color mode:
Productlane.init({
widgetKey: "<your-widget-key />",
mode: "light",
});Supported values are "light", "dark", and "auto". The "auto" option adapts to the user's operating system theme preference.
You can also change the widget theme dynamically using the
Productlane.setmethod.
To control where the widget appears on your site, use the position option. These setting let you choose which side of the screen the widget button appears on and which direction the widget opens.
position ("left" , "right" or "center"):
Sets the default side of the screen where the widget button is placed. The widget panel will open in the opposite direction relative to the button and remain close to it, or appear centered if position is set to "center".
For example, position: "right" places the button on the right edge of the screen, and the widget opens toward the left.
Here is an example of widget positioning:
Productlane.init({
widgetKey: "<your-widget-key />",
position: "left",
});To fine-tune the widget's distance from the edges of the screen, use the offset option. This allows you to add custom spacing from the bottom, left, or right edges.
Productlane.init({
widgetKey: "<your-widget-key />",
offset: {
bottom: "20px",
right: "20px",
},
});
Property | Type | Description |
|---|---|---|
| string | Distance from the bottom edge (e.g., |
| string | Distance from the left edge (e.g., |
| string | Distance from the right edge (e.g., |
All properties are optional. Use left or right depending on your position setting. For example, if your widget is positioned on the left, use left and bottom to adjust its placement:
Productlane.init({
widgetKey: "<your-widget-key />",
position: "left",
offset: {
bottom: "80px",
left: "16px",
},
});If you want to prevent the changelog notification popup from appearing and instead have the widget display as usual, you can use the disableChangelogNotification option. This can be useful, for example, when you want to hide the popup for selected users.
Productlane.init({
widgetKey: "<your-widget-key />",
disableChangelogNotification: true,
});When set to true, the changelog popup will not appear, and the widget will behave normally. Setting it to false (or omitting it) will keep the default behavior where changelog popups are shown when new changelog were published.
Once the widget is initialized, you can use these methods to interact with it.
Productlane.open(view?: "INDEX" | "CHANGELOG" | "DOCS" | "FEEDBACK" | "AICHAT" | "LIVE_CHAT")
Opens the Productlane widget. By default, it opens the last active view, but you can optionally specify a view to open directly:
"INDEX" – opens the main index view with all navigation links.
"CHANGELOG" – opens the latest changelog.
"DOCS" – opens the documentation navigation menu. (Use Productlane.openDocs() method to open a specific article.)
"FEEDBACK" – opens the contact form where users can leave feedback, report bugs, or ask questions.
The
viewargument is case-insensitive, so"changelog","Changelog", and"CHANGELOG"all work the same.
Productlane.close() Closes the widget.
Productlane.toggle() Toggles between opening and closing the widget.
Productlane.disable() Disables the widget across the entire page.
Productlane.enable() Enables the widget.
Productlane.openDocs(documentUrlName | documentId) To open a specific article. You can pass either:
documentUrlName – the URL path of the article, like "get-started/quickstart"
documentId – the internal ID of the document
Productlane.set() For setting certain widget properties. Equal to the settings object in Productlane.init().
Productlane.init({
widgetKey: "<your-widget-key />",
});
Productlane.on("loaded", () => {
Productlane.set({
user: {
email: "[email protected]",
},
});
});Make sure to call any methods after the widget is initialized, e.g. like so:
Productlane.on('loaded', () => {/* Your code here */}).
You can listen to the following events:
Productlane.on("loaded", () => {}) Fires when the widget is fully loaded. Make sure to call any methods in the callback of this event.
Productlane.on("opened", () => {}) Fires after the widget is opened.
Productlane.off("opened", () => {}) Removes event listener for opened event.
Productlane.on("closed", () => {}) Fires after the widget is closed.
Productlane.off("closed", () => {}) Removes event listener for closed event.
Productlane.on("toggled", () => {}) Fires after the widget is toggled.
Productlane.off("toggled", () => {}) Removes event listener for toggled.
Productlane.on("customLinkClicked", (link) => {})
Productlane.on("openDocsPage", ({id, title, urlName}) => {}) Fires when a docs article is opened.
Productlane.on("customLinkClicked", (link) => {
console.log(link); // { "title": "Docs", "url": "productlane.com/docs" }
});Productlane.off("customLinkClicked", () => {}) Removes the event listener for the customLinkClicked event.