This package has been deprecated

Author message:

Package has been renamed to @kontent-ai/smart-link. Please update your code to use the latest version.

TypeScript icon, indicating that this package has built-in type declarations

2.3.1 • Public • Published

Kontent Smart Link SDK

licence npm downloads jsdelivr snyk

Usage | Contributing | Troubleshooting | Breaking changes

Kontent Smart Link SDK can be used to automatically inject smart links to Kentico Kontent according to manually specified HTML data attributes on your website. It also lets you connect your website with Web Spotlight for faster editing and preview of your content.

⚠️ Kontent Smart Link SDK is a browser-only SDK, which means that the Node.js environment is not currently supported. Make sure to always initialize the Smart Link SDK in a browser context.


You can install this library using npm or using global CDNs such as jsdelivr.


npm i @kentico/kontent-smart-link

UMD Bundles

When using the UMD bundle and including this library inside the script tag of your HTML page, you can then find an SDK instance under the KontentSmartLink global variable. JS bundle and its minified version are distributed in dist folder.

  • kontent-smart-link.umd.min.js
  • kontent-smart-link.umd.js



Gzip browser bundle

Gzip browser bundle


Kontent Smart Link SDK parses manually specified HTML data attributes on your webpage and automatically injects smart links to Kentico Kontent. Injecting smart links to Kontent means that all elements marked with special data attributes will become interactive (handle clicks/redirect to Kontent/navigate from the preview in Web Spotlight/etc.). The type of injected smart link depends on used data attributes, their hierarchy, and context (Web Spotlight).

Data attributes

Kontent Smart Link SDK highly depends on a set of manually specified data attributes in your HTML markup. That is why it won't work properly without those attributes. The SDK won't add the data attributes to your HTML, you must add them yourself so that SDK will then be able to use them as a source of data (e.g. Kontent project ID, element code name, etc.) when injecting the smart links.

Available data attributes

Attribute Value Description
data-kontent-project-id guid Kontent project/environment ID.
data-kontent-language-codename string Kontent language codename.
data-kontent-item-id guid Content item ID.
data-kontent-component-id guid Content component ID.
data-kontent-element-codename string Content type element codename.
data-kontent-add-button - Specifies that node should have add-button rendered near it.
data-kontent-add-button-insert-position start | before | after | end Specifies the insert position of an item/content component added using add button.
data-kontent-add-button-render-position bottom-start | bottom | bottom-end | left-start | left | left-end | top-start | top | top-end | right-start | right | right-end Specifies visual location of add button.
data-kontent-disable-features highlight Specifies that the selected node should not have highlight (which includes edit buttons). Useful when there are too many smart links on your page.

Data attributes hierarchy

Although it is possible to put all previously specified data attributes on the same DOM node, you don't have to do it. We recommend you set data attributes hierarchically so that you don't have to duplicate the same attributes.

For example, your webpage probably represents one specific project in Kontent, which means that you can place data-kontent-project-id attribute on your <body> element or another wrapping DOM node so that all descendant nodes inherit this project ID. The same could be true for a language code name. If your page uses only 1 language variant at a time, you could place your data-kontent-language-codename attribute next to your data-kontent-project-id from a previous step. But remember, that since language variant is relevant to some specific project, data-kontent-language-codename attribute should always be on the same element as data-kontent-project-id attribute or on some of its descendants. After that, you can find all DOM nodes that represent Kontent items and place data-kontent-item-id attribute on them. Then inside those nodes, you can find all descendants that represent some element of the Kontent item and put data-kontent-element-codename attribute on them. In the case of Rich Text elements and Linked Items elements, there could be other content items or content components inside them, which have their own elements and so on.

Content components

Content component is single-use content, that is also sometimes referred to as one-off, channel-specific, or non-reusable. Content components exist only within a specific rich text element in your content items and become their integral part. This means you won't find components in your list of items in Content Inventory in Kontent.

You should use data-kontent-component-id attribute to specify that something represents a content component in your HTML so that the SDK knows that this item has no separate page in the Kontent and must be opened in the context of its parent content item.

Smart link types

Currently, there are 4 types of smart links supported by Kontent Smart Link SDK. All of them require certain data attributes to be specified in HTML markup of your webpage. Please note, that most of those smart link types are only available and visible inside Web Spotlight preview iframe.

Edit element button

Edit element button allows you to edit a specific element of a content item by clicking on it in preview. Inside Web Spotlight, this will lead to In-Context editor being opened, and the selected element will be scrolled into view. Outside Web Spotlight, you will be redirected to Kontent item editor.

Data attributes: data-kontent-project-iddata-kontent-language-codenamedata-kontent-item-iddata-kontent-component-id?data-kontent-element-codename.

Environment: This feature is available both inside and outside Web Spotlight.

Edit content component button

Edit content component button allows you to edit a specific content component by clicking on it in preview. This will lead to In-Context editor being opened, and the selected content component scrolled into view.

Data attributes: data-kontent-project-iddata-kontent-language-codenamedata-kontent-item-iddata-kontent-component-id.

Environment: This feature is only available inside Web Spotlight.

Edit content item button

Edit content item button allows you to edit a specific content item by clicking on it in preview. This will lead to In-Context editor being opened.

Data attributes: data-kontent-project-iddata-kontent-language-codenamedata-kontent-item-id.

Environment: This feature is only available inside Web Spotlight.

Add button

Add button allows you to add content to your page right from your preview. It supports both Linked items elements and Rich Text elements.

Environment: This feature is only available inside Web Spotlight.

Fixed add button

Data attributes: data-kontent-project-iddata-kontent-language-codenamedata-kontent-item-iddata-kontent-component-id?data-kontent-element-codename(RTE or LIE) → data-kontent-add-button & data-kontent-add-button-render-position? & data-kontent-add-button-insert-position=start|end .

Relative add button

Relative add button allows you to add content relatively to some existing content in your Rich Text element or Linked item element. For example, you can insert a new content component after or before the existing content component in RTE. To turn add button into a relative add button, you need to set insert position to before or after and provide target id on the same node using data-kontent-item-id or data-kontent-component-id attribute.

Data attributes: data-kontent-project-iddata-kontent-language-codenamedata-kontent-item-iddata-kontent-component-id?data-kontent-element-codename(RTE or LIE) → data-kontent-item-id|data-kontent-component-id(target item) & data-kontent-add-button & data-kontent-add-button-render-position? & data-kontent-add-button-insert-position=before|after.

SDK Initialization

After all data attributes have been set, you can initialize Kontent Smart Link SDK on your website. You can use initialize or initializeOnLoad method in order to do it. Both of the previously mentioned methods return an instance of initialized SDK (initializeOnLoad returns a Promise resolving to an instance). The main difference between the two methods is that the initializedOnLoad method will wait for the page to load before initializing the SDK. This can be useful when you want to initialize the SDK in the head section of your webpage when the page has not been fully loaded yet.

Kontent Smart Link SDK uses multiple event listeners, timeouts, observers to track the position of the relevant elements, so please always call .destroy() method to dispose all of those side effects before trying to initialize the SDK again (e.g. inside useEffect cleanup function) to avoid memory leaks.


Both initialization methods take an optional configuration argument, that you can use to configure the SDK. You can also use instance setConfiguration method to update configuration of initialized SDK.

Attribute Default Description
debug false When it's set to true, enables all debug logs. Can be useful to get more information about how the SDK works inside, but can affect performance.
defaultDataAttributes { projectId: undefined, languageCodename: undefined } Default values for data attributes, which are only used when those data attributes are not found in DOM during data attributes parsing process. For now, only projectId and languageCodename attributes are supported.
queryParam ksl-enabled Name of the query parameter that must be present in the URL to turn the smart link injection on. It is not necessary for query parameter to have a truthy value (just the presence of this query parameter is checked). If set to falsy value ('', null), the smart link injection will always be enabled. Query parameter is only used outside Web Spotlight.


The following custom CSS properties can be used to customize the visuals of the SDK output.

Custom property Default Description
--ksl-color-background-default rgba(255, 255, 255, 1) Default background color used in toolbar and popover.
--ksl-color-background-default-disabled rgba(223, 223, 223, 1) Disabled background color for buttons inside toolbar and popover.
--ksl-color-background-default-hover rgba(21, 21, 21, 0.1) Hover background color for buttons inside toolbar and popover.
--ksl-color-background-default-selected rgba(255, 240, 239, 1) Selected background color for buttons inside toolbar and popover.
--ksl-color-background-secondary rgba(20, 22, 25, 1) Secondary background color used in tooltips.
--ksl-color-primary rgba(219, 60, 0, 1) Primary color used as a hover border color in highlights and as a background color in add buttons.
--ksl-color-primary-hover rgba(149, 48, 0, 1) Primary color used as a hover background color in add buttons.
--ksl-color-primary-transparent rgba(219, 60, 0, 0.5) Primary color with transparency used as a default border color in highlights.
--ksl-color-text-default rgba(255, 255, 255, 1) Text color used on a default background (buttons inside toolbar and popover).
--ksl-color-text-default-disabled rgba(140, 140, 140, 1) Disabled text color used on a default background.
--ksl-color-text-secondary rgba(21, 21, 21, 1) Text color used inside tooltips and add buttons.
--ksl-shadow-default 0 8px 32px rgba(16, 33, 60, 0.24), 0 0 8px rgba(0, 0, 0, 0.03) Default shadow for toolbar.
--ksl-shadow-primary 0 8px 10px rgba(219, 60, 0, 0.2), 0 6px 20px rgba(219, 60, 0, 0.12), 0 8px 14px rgba(219, 60, 0, 0.14) Shadow for add buttons.
--ksl-z-index 9000 Base value of z-index used for calculation of individual values for each ksl-element type

For example, if you want to override all SDK colors and shadows for all SDK elements on the page, you can do it by changing the values of all available custom properties of a :root element in your CSS or inside a new <style> tag on your page.

:root {
    --ksl-color-background-default: rgba(4, 102, 200, 1);
    --ksl-color-background-default-disabled: rgba(2, 62, 125, 1);
    --ksl-color-background-default-hover: rgba(0, 40, 85, 0.1);
    --ksl-color-background-secondary: rgba(2, 62, 125, 1);
    --ksl-color-background-default-selected: rgba(3, 83, 164, .1);
    --ksl-color-primary: rgba(4, 102, 200, 1);
    --ksl-color-primary-transparent: rgba(4, 102, 200, 0.5);
    --ksl-color-primary-hover: rgba(2, 62, 125, 1);
    --ksl-color-text-default: rgba(255, 255, 255, 1);
    --ksl-color-text-default-disabled: rgba(51, 65, 92, 1);
    --ksl-color-text-secondary: rgba(255, 255, 255, 1);
    --ksl-shadow-default: 0 8px 32px rgba(0, 24, 69, 0.24), 0 0 8px rgba(0, 0, 0, 0.03);
    --ksl-shadow-primary: 0 8px 10px rgba(4, 102, 200, 0.2), 0 6px 20px rgba(4, 102, 200, 0.12), 0 8px 14px rgba(4, 102, 200, 0.14);
    --ksl-z-index: 9000;

Preview autorefresh in Web Spotlight

When working with the in-context editor in Web Spotlight, it is good to keep the content of your preview fresh without having to refresh it manually after every change. Starting from version 2.2.0, the Smart Link SDK supports the preview autorefresh feature in Web Spotlight.

For your web apps to support the preview autorefresh feature, make sure that your preview environment:

  1. Uses the latest version of the Smart Link SDK.
  2. Has the X-KC-Wait-For-Loading-New-Content header set to true when fetching data from Delivery Preview API.

If both previously mentioned conditions are met, Web Spotlight will wait for your changes to be ready via the Delivery Preview API, and after that, the preview will be refreshed automatically.

Implementing custom refresh handler

In some cases, simply refreshing the page might not be enough. For example, if you use a static site generator for your preview, you need to trigger the rebuild of the page before refreshing it. Or maybe you don't want to refresh the entire page when a single item has been updated and would rather re-render only the affected place in the UI. In some cases, simply refreshing the preview page after the change in Web Spotlight is not enough.

That's why the Smart Link SDK supports the custom refresh handler, which allows you to specify how your web page reacts to refresh events received from Web Spotlight. If you register a custom refresh handler, it will be called instead of a default handler every time when refresh is triggered in Web Spotlight (both manually and automatically).

You can implement a custom refresh handler using the .on(KontentSmartLinkEvent.Refresh, (data, metadata, originalRefresh) => {}) method on the SDK instance:

import KontentSmartLink, { KontentSmartLinkEvent } from '@kentico/kontent-smart-link';

const sdk = KontentSmartLink.initialize({ ... });

sdk.on(KontentSmartLinkEvent.Refresh, (data, metadata, originalRefresh) => {
  // your custom refresh logic

A custom refresh handler takes three arguments:

Argument Type Description
Data { projectId: string, languageCodename: string, updatedItemCodename: string } | undefined Information about updated item, that caused autorefresh. It is only available when refresh is triggered automatically.
Metadata { manualRefresh: boolean } Manual refresh is set to true when the refresh is triggered by user.
Original refresh () => void Default refresh handler.

You can then unregister the custom refresh handler using the .off method on the SDK instance.

Re-fetching item data without fully refreshing in React

It is possible to only update the affected place of the UI by re-fetching page data instead of refreshing the whole page. The following code sample shows how this could be implemented in a React application.

import KontentSmartLink, { KontentSmartLinkEvent } from '@kentico/kontent-smart-link';

const PageContent: React.FC = () => {
  const [data, setData] = useState(null);
  const fetchData = useCallback((projectId, languageCodename, itemCodename) => {...}, []);

  useEffect(() => {
    const sdk = KontentSmartLink.initialize();

    // register custom refresh handler to avoid full refresh in some cases
    sdk.on(KontentSmartLinkEvent.Refresh, (data: IRefreshMessageData, metadata: IRefreshMessageMetadata, originalRefresh: () => void) => {
      // if user triggered the refresh manually, just refresh the page
      if (metadata.manualRefresh) {
      } else {
        // refetch data for the updated item, instead of refreshing the whole page
        const { projectId, languageCodename, updatedItemCodename } = data;
        fetchData(projectId, languageCodename, updatedItemCodename);

    return () => {
  }, [fetchData]);

  return (...);
Sending request to rebuild the SSG page before refreshing it (Netlify + Gatsby)

When using static site generators, you have to rebuild your website in order to apply your changes. The following example shows how this could be done using a custom refresh handler. In this example, we used Gatsby deployed to Netlify, but the solution for other SSG frameworks should be similar.

The deploy-status function is a custom Netlify function used to get the status of the last deploy, so that we can check if the deployment is finished and the page can be refreshed.

// ./.netlify/functions/deploy-status.js
import fetch from 'node-fetch';

const siteId = process.env.NETLIFY_SITE_ID;
const token = process.env.NETLIFY_TOKEN;

const handler = async event => {
  try {
    const endpoint = `${siteId}/deploys`;
    const result = await fetch(endpoint, {
      headers: {
        Authorization: `Bearer ${token}`,

    const data = await result.json();

    // first entry is last deploy
    const deploy = {
      state: data[0].state,

    return {
      statusCode: 200,
      body: JSON.stringify(deploy),
  } catch (error) {
    return { statusCode: 500, body: error.toString() };

module.exports = { handler };

We trigger rebuild process inside our custom refresh handler and wait for the deployment process to finish. After that the page can be refreshed using the originalRefresh callback.

// inside a component used as a wrapper for all pages

// This function triggers the build hook defined on Netlify, which
// starts deployment process on Netlify. This is required to get new data
// to preview.
// This is just the PoC. In a real app, we need to kill the previous active deploy, before
// starting a new one.
const triggerRebuildOnNetlifyAndWaitForDeploy = useCallback(() => {
  fetch('{HOOK_ID}?trigger_title=autorefresh', {
    method: 'POST',
  }).then(() => {
    async function checkDeployStatus() {
      // 'deploy-status' is a custom Netlify function that returns status of the last deploy
      const buildReq = await fetch('/.netlify/functions/deploy-status');
      const buildData = await buildReq.json();

      // display a loader to users to let them know rebuild is in progress
      setRebuildInProgress(buildData.state !== 'ready');

      if (buildData.state === 'ready') {
      } else {
        setTimeout(checkDeployStatus, 3000);

}, []);

useEffect(() => {
  const plugin = KontentSmartLink.initialize({
    queryParam: 'preview-mode',

  // custom refresh handler
  plugin.on('refresh', (data, metadata, originalReload) => {
    // You can trigger the rebuild of the page, wait for it to finish and refresh the page after that.
    // Please consider displaying some sort of loader to your users, in case rebuild process takes time, to let them
    // know that the refresh is in progress.

  return () => {
}, [triggerRebuildOnNetlifyAndWaitForDeploy]);

For even better experience, you can combine the previous two methods and re-fetch content from Delivery Preview API on the client-side to update affected placed while waiting for the rebuild process to finish.

Using SDK inside and outside Web Spotlight

When Kontent Smart Link SDK is used outside Web Spotlight, it listens to the query parameters in the URL to toggle smart link injection. The name of the query parameter defaults to ksl-enabled, but can be changed using the queryParam configuration argument of the initialize or initializeOnLoad methods. Only the presence of the query parameter is checked and its value is ignored, so all the following options are valid: ?ksl-enabled=true, ?ksl-enabled=false , ?ksl-enabled, etc.

If you set the query parameter to a false value (null, ""), then the SDK will always be enabled.

If the SDK detects it is run inside an iframe at the beginning of the initialization process, it will try to connect to the Web Spotlight by sending an iframe message to the parent window. If Web Spotlight response is received, query parameter detection will be turned off and additional features (more in the smart link types section) will be enabled. Else the SDK will continue to work as if it was outside Web Spotlight (query parameters detection, redirects to Kontent, etc.)

IFrame Communication

When running inside Web Spotlight preview iframe, Kontent Smart Link SDK enables several additional features and sends iframe messages instead of redirecting user to Kontent page. All message types are listed below.

Message Data Origin Description
kontent-smart-link:initialized { projectId: string | null, languageCodename: string | null, enabled: boolean } SDK This message is sent by the SDK when it is initialized.
kontent-smart-link:initialized:response - Host This message is sent by the host as a response to initialized message.
kontent-smart-link:status { enabled: boolean } Host This message is used to toggle the SDK features.
kontent-smart-link:element:clicked { projectId: string, languageCodename: string, itemId: string, contentComponentId?: string, elementCodename: string } SDK This message is sent by the SDK when element with data-kontent-element-codename attribute is clicked.
kontent-smart-link:content-component:clicked { projectId: string, languageCodename: string, itemId: string, contentComponentId: string } SDK This message is sent by the SDK when element with data-kontent-component-id attribute is clicked.
kontent-smart-link:content-item:clicked { projectId: string, languageCodename: string, itemId: string } SDK This message is sent by the SDK when element with data-kontent-item-id attribute is clicked.
kontent-smart-link:add:initial { projectId: string, languageCodename: string, itemId: string, contentComponentId?: string, elementCodename: string, insertPosition: { targetId?: string, placement: 'start' | 'end' | 'before' | 'after', } } SDK This message is sent by the SDK when add button is clicked.
kontent-smart-link:add:initial:response { elementType: 'LinkedItems' | 'RichText' | 'Unknown', isParentPublished: boolean, permissions: Map<string,string> } Host This message is sent by the host as a response to initial add button click.
kontent-smart-link:add:action { projectId: string, languageCodename: string, itemId: string, contentComponentId?: string, elementCodename: string, action: string, insertPosition: { targetId?: string, placement: 'start' | 'end' | 'before' | 'after', } } SDK This message is sent by the SDK when add button action is clicked.
kontent-smart-link:preview:refresh { projectId: string, languageCodename: string, updatedItemCodename: string } | undefined Host This message is sent when preview has to be refreshed.

Nested iframes

There may be some cases when you would want to put your page into another iframe (e.g. to simulate a mobile device resolution). But if you then load your nested iframe page inside Web Spotlight preview tab, it would act as if it wasn't inside Web Spotlight. This happens because the initialization message sent from SDK to Kontent gets lost in the parent iframe. You can use the following workaround to fix the issue:



    <title>Kontent Smart Link - HTML example</title>
    <script type="text/javascript"
    <script type="text/javascript">
      KontentSmartLink.initializeOnLoad({ queryParam: "preview" });
  <body data-kontent-project-id="1d50a0f7-9033-48f3-a96e-7771c73f9683" data-kontent-language-codename="en-US">
    <nav class="navigation" data-kontent-item-id="6ea11626-336d-47e5-9f35-2d44fa1ad6d6">
      <img class="navigation__logo" data-kontent-element-codename="logo" />
        <li class="navigation__list-item" data-kontent-component-id="036acd8f-5e6d-4023-b0f8-a4b8e0b573b1">
          <span data-kontent-element-codename="title">Home</span>
        <li class="navigation__list-item" data-kontent-component-id="f539f1bc-9dc4-4df5-8876-dbb1de5ae6eb">
          <span data-kontent-element-codename="title">About us</span>
        <img class="home__banner" data-kontent-element-codename="image" />
        <h1 data-kontent-element-codename="title">Home page</h1>
        <p data-kontent-element-codename="text">...</p>


import KontentSmartLink from "@kentico/kontent-smart-link";

// This is just an example of SDK initialization inside ES6 module.
// HTML markup should still contain all necessary data-attributes.
const kontentSmartLink = KontentSmartLink.initializeOnLoad({
  debug: true,
  defaultDataAttributes: {
    projectId: "1d50a0f7-9033-48f3-a96e-7771c73f9683",
    languageCodename: "default",
  queryParam: "ksl-preview"


In order to use the SDK with the Next.js framework you can either initialize it separately on each page or initialize it once for the whole application using the _app.jsx file. Do not forget to destroy() SDK for it to work properly.

// _app.jsx
import KontentSmartLink from "@kentico/kontent-smart-link";

const MyApp = ({
}) => {
  useEffect(() => {
    // This is just an example of SDK initialization inside ES6 module.
    // HTML markup should still contain all necessary data-attributes (e.g. PageSection component).
    const kontentSmartLink = KontentSmartLink.initialize({
      defaultDataAttributes: {
        projectId: "1d50a0f7-9033-48f3-a96e-7771c73f9683",
        languageCodename: "default",
      queryParam: "preview-mode"

    return () => {

  return (
      <Component {...pageProps} />

const PageSection = (props) => {
  return (
    <div data-kontent-item-id="3fdbc5a0-13e6-4516-82c3-50bf4db43644">
      <div data-kontent-element-codename="page_section__content">

Additionally, you may encounter an issue with SameSite cookies not being set correctly. This can be solved by utilizing the code snippet below in your API route handling preview file to replace SameSite=Lax to SameSite=None; Secure; right after res.setPreviewData call.

const setCookieSameSite = (res, value) => {
    const cookies = res.getHeader("Set-Cookie");
    const updatedCookies = cookies?.map((cookie) =>
            `SameSite=${value}; Secure;`
export default function handler(req, res) {
    // ...

    setCookieSameSite(res, "None");
    // ...


You can either initialize the SDK on every page or use a layout to initialize the SDK while using Gatsby. Do not forget to destroy() SDK for it to work properly.

// src/components/layout.jsx
import KontentSmartLink from "@kentico/kontent-smart-link";

export default function Layout({ children }) {
  useEffect(() => {
    // This is just an example of SDK initialization inside ES6 module.
    // HTML markup should still contain all necessary data-attributes (e.g. .layout element).
    const kontentSmartLink = KontentSmartLink.initialize({
      queryParam: "enable-ksl-sdk"
    return () => {

  return (


Unit tests

Since this SDK highly depends on browser APIs, the unit tests are run by Karma test runner (+ Jasmine) inside Chrome browser. To run all tests in a watch mode you can use the npm run test:unit command. To run all tests only once you can use the npm run test:unit:ci command. All unit tests are located in the test-browser folder.

Visual regression tests

Visual regression testing is implemented using Storybook and Loki. Each story in Storybook represents a test case, which is then used by Loki to generate screenshots. In order to run visual regression tests you need to start Storybook using the npm run storybook command and then start loki testing using the npm run test:visual command. Or you can use the npm run test:visual:ci command to automatically start the Storybook server in a CI mode and run visual tests.

Visual regression tests use the built version of SDK, so before running them make sure you rebuild the SDK after the last change you made. You can this using the npm run build command or using the npm run dev command to start build in a watch mode.

Breaking changes

All breaking changes can be found in a separate markdown file.

Feedback & Contribution

Feedback & Contributions are welcomed. Feel free to take/start an issue & submit PR.


npm i @kentico/kontent-smart-link

DownloadsWeekly Downloads






Unpacked Size

1.76 MB

Total Files


Last publish


  • enngage
  • hom3r
  • honzabarton
  • ivannk
  • janck
  • jirik
  • kentico_tomasw
  • martins1
  • simply007
  • vladislavb-kentico
  • xp-peterp
  • xperience-npmjs-publisher