The Looker extension framework will soon use a new loading mechanism. The new loader may cause errors when existing extensions are loaded. For instructions on how to test your extensions with the new loader before it is officially turned on in Looker environments, see the Testing the new extension framework loader article in the Looker Help Center.
This page provides code examples written in React and JavaScript for common functions you may want to utilize in your extensions.
Using the Looker Extension SDK
To add functions from the Looker Extension SDK, first you need to get a reference to the SDK, which can be done either from the provider or globally. Then you can call SDK functions as you would in any JavaScript application.
To access the SDK from the provider:
import { ExtensionContext2 } from '@looker/extension-sdk-react' export const Comp1 = () => { const extensionContext = useContext( ExtensionContext2 ) const { extensionSDK, coreSDK } = extensionContextTo access the SDK globally (the extension must be initialized before this is called):
const coreSDK = getCoreSDK2()
Now you can use the SDK as you would in any JavaScript application:
Navigating elsewhere in the Looker instance
Since the extension runs in a sandboxed iframe, you cannot navigate elsewhere within the Looker instance by updating the parent’s window.location
object. It is possible to navigate using the Looker Extension SDK.
This function requires the navigation
entitlement.
Opening a new browser window
Since the extension runs in a sandboxed iframe, you cannot use the parent window to open a new browser window. It is possible to open up a browser window using the Looker Extension SDK.
This function requires either the new_window
entitlement to open a new window to a location in the current Looker instance, or the new_window_external_urls
entitlement to open a new window that runs on a different host.
Routing and deep linking
The following applies to React-based extensions.
The ExtensionProvider
and ExtensionProvider2
components automatically create a React Router called MemoryRouter
for you to use. Do not attempt to create a BrowserRouter
, as it does not work in sandboxed iframes. Do not attempt to create a HashRouter
, as it it does not work in sandboxed iframes for the non-Chromium-based version of the Microsoft Edge browser.
If the MemoryRouter
is utilized and you use react-router
in your extension, the extension framework will automatically sync your extension’s router to the Looker host router. This means that the extension will be notified of browser backward and forward button clicks and of the current route when the page is reloaded. This also means that the extension should automatically support deep linking. See the extension examples for how to utilize react-router
.
Extension context data
Extension framework context data should not be confused with React contexts.
Extensions have the ability to share context data between all users of an extension. The context data can be used for data that does not change frequently and that does not have special security requirements. Care should be taken when writing the data, as there is no data locking and the last write wins. The context data is available to the extension immediately upon startup. The Looker Extension SDK provides functions to allow the context data to be updated and refreshed.
The maximum size of the context data is approximately 16 MB. Context data will be serialized to a JSON string, so that also needs to be taken into account if you use context data for your extension.
User attributes
The Looker Extension SDK provides an API to access Looker user attributes. There are two types of user attribute access:
Scoped — Associated with the extension. A scoped user attribute is namespaced to the extension and the user attribute must be defined in the Looker instance before it can be used. To namespace a user attribute, prefix the attribute name with the extension name. Any dash and the ‘::’ characters in the extension name must be replaced by an underscore, since dashes and colons cannot be used in user attribute names.
For example: a scoped user attribute named
my_value
used with an extension id ofmy-extension::my-extension
must have a user attribute name ofmy_extension_my_extension_my_value
defined. Once defined, the user attribute may be read and updated by the extension.Global — These are global user attributes and are read only. An example is the
locale
user attribute.
Below is a list of user attributes API calls:
userAttributeGetItem
— Reads a user attribute. A default value may be defined and will be used if a user attribute value does not exist for the user.userAttributeSetItem
— Saves a user attribute for the current user. Will fail for global user attributes. The saved value is only visible to the current user.userAttributeResetItem
— Resets a user attribute for the current user to the default value. Will fail for global user attributes.
To access user attributes, you must specify the attribute names in the global_user_attributes
and/or scoped_user_attributes
entitlements. For example, in the LookML project manifest file, you would add:
Local storage
Sandboxed iframes do not allow access to browser local storage. The Looker Extension SDK allows an extension to read and write to the parent window’s local storage. Local storage is namespaced to the extension, meaning it cannot read local storage created by the parent window or other extensions.
Using local storage requires the local_storage
entitlement.
The extension localhost API is asynchronous as opposed to the synchronous browser local storage API.
Updating the page title
Extensions may update the current page title. Entitlements are not required to perform this action.
Writing to the system clipboard
Sandboxed iframes do not allow access to the system clipboard. The Looker Extension SDK allows an extension to write text to the system clipboard. For security purposes, the extension is not allowed to read from the system clipboard.
To write to the system clipboard, you need the use_clipboard
entitlement.
Embedding dashboards, Looks, and Explores
The extension framework supports embedding of dashboards, Looks, and Explores. Both regular dashboards and legacy dashboards can be embedded.
The use_embeds
entitlement is required. We recommend that you use the Looker JavaScript Embed SDK to embed content. See the Embed SDK documentation for more information.
The extension examples use styled components to provide simple styling to the generated iframe. For example:
Accessing external API endpoints
The extension framework provides two methods for accessing external API endpoints:
- The server proxy — Accesses the endpoint via the Looker server. This mechanism allows client IDs and secret keys to be set securely by the Looker server.
- The fetch proxy — Accesses the endpoint from the user’s browser. The proxy is the Looker UI.
In both cases you need to specify the external API endpoint in the extension external_api_urls
entitlement.
Server proxy
The following example demonstrates the use of the server proxy to get an access token for use by the fetch proxy. The client id and secret must be defined as user attributes for the extension. Typically, when the user attribute is set up, the default value is set to the client id or secret.
The user attribute name must be mapped to the extension. Dashes must be replaced by underscores and the ::
characters must be replaced with a single underscore.
For example, if the name of your extension is my-extension::my-extension
, the user attributes that need to be defined for the above example would be:
Fetch proxy
The following example demonstrates the use of the fetch proxy. It uses the access token from the previous server proxy example.
OAuth integration
The extension framework supports integration with OAuth providers. OAuth can be used to get an access token to access a particular resource, for example a Goorgle sheets document.
You will need to specify the OAuth server endpoint in the extension oauth2_urls
entitlement. You may also need to specify additional URLs in the external_api_urls
entitlement.
The extension frameworks supports the following flows:
- Implicit flow
- Authorization code grant type with secret key
- PKCE code challenge and verifier
The general flow is that a child window is opened that loads an OAuth server page. The OAuth server authenticates the user and redirects back to the Looker server with additional details that can be used to get an access token.
Implicit flow:
Authorization code grant type with secret key:
PKCE code challenge and verifier:
Spartan
Spartan refers to a method of using the Looker instance as an environment to expose extensions, and extensions only, to a designated set of users. A spartan user navigating to a Looker instance will be presented with whatever login flow the Looker admin has configured. Once the user is authenticated, an extension will be presented to the user according to their landing_page
user attribute as shown below. The user can only access extensions; they cannot access any other part of Looker. If the user has access to multiple extensions, the extensions control the user’s ability to navigate to the other extensions using extensionSDK.updateLocation
. There is one specific Looker Extension SDK method to allow the user to log out of the Looker instance.
Defining spartan users
In order to define a spartan user, you must create a group called “Extensions Only”.
Once the “Extensions Only” group has been created, navigate to the User Attributes page in Looker’s Admin section and edit the landing_page
user attribute. Select the Group Values tab and add the “Extensions Only” group. The value should be set to /spartan/my_extension::my_extension/
where my_extension::my_extension
is the id of your extension. Now when that user logs in, the user will be routed to the designated extension.
Code splitting
Code splitting is the technique where code is requested only when it is needed. Typically code chunks are associated with React routes where each route gets its own code chunk. In React this is done with the Suspense
and React.lazy
components. The Suspense
component displays a fallback component while the code chunk is loaded. React.lazy
is responsible for loading the code chunk.
Setting up to code split:
The lazy loaded component is implemented as follows:
The component is implemented as follows. The component must be exported as a default component:
Tree shaking
Looker SDKs do support tree shaking but they are not yet perfect. We are continually modifying our SDKs to improve tree shaking support. Some of these changes may require that you refactor your code to take advantage, but when this is required, it will be documented in the release notes.
To utilize tree shaking the module you are using must be exported as an esmodule and the functions you import must be free of side effects. The Looker SDK for Typescript/Javascript, Looker SDK Runtime Library, Looker UI Components, Looker Extension SDK, and Extension SDK for React all do this.
In an extension, you should pick one of the Looker SDKs, 3.1 or 4.0 and use the ExtensionProvider2
component from the Extension SDK for React. If you require both SDKs, continue to use the ExtensionProvider
component, but you will see an increase in the final bundle size.
The following code sets up the extension provider. You will need to tell the provider which SDK you want:
Do not use the following import style in your extension:
The above example brings in everything from the module. Instead, only import the components you actually need. For example:
Glossary
- Code splitting — A technique for lazy loading of JavaScript until it is actually needed. Ideally, you want to keep the initially loaded JavaScript bundle as small as possible. This can be achieved by utilizing code splitting. Any functionality that is not immediately required is not loaded until it is actually needed.
- IDE — Integrated development environment. An editor used to create and modify an extension. Examples are Visual Studio Code, Intellij, and WebStorm.
- Scene — Generally a page view in Looker. Scenes map to major routes. Sometimes a scene will have child scenes that map to subroutes within the major route.
- Transpile — The process of taking source code written in one language and transforming it into another language that has a similar level of abstraction. An example is TypeScript to JavaScript.