Skip to main content

Use TypeScript API to configure custom components

Learn how to configure custom React components with TypeScript API.

You can use the TypeScript API to add properties, pass data, and set up event handlers for your custom components. You can also enhance your custom component library, designate a primary instance or Space for deployment, and include custom headers or cookies in self-hosted Retool deployments.

Add properties to custom components

You can add properties to custom components that pass data to and from the Retool app using the TypeScript API.

export const HelloWorldComponent: FC = () => {
// Allows the builder to specify a "name" property on each component they build with.
// The builder can then pass data from their Retool app into the component by setting a value
// for the "name" property.
const [name, setName] = Retool.useStateString({
name: "name",
});

return (
<div>
<div>Hello {name}!</div>
</div>
);
};

The above component has a name property that any builder can then pass data into, like any other Retool component. For example, you can assign the value of name to be {{query[0].name}}, which sets name to be the result of a query in your Retool app. In the below example, {{query[0].name}} resolves to Daniel, which the custom component then displays.

Setting name to query[0].name

To pass data from the component back to your Retool app, you can use the setName method to set the value of name from within your custom component, and then reference this value in the rest of your Retool app using {{yourCustomComponentId.name}}.

TypeScript API

Custom components can interact with the Retool app they are added to. They can fetch their state from Retool, set their default size when dragged onto the canvas, or tell Retool that one of their events has fired. There are separate TypeScript methods for each of these actions.

Pass data into a custom component

The following useState functions allow you to pass data from your Retool app into your custom component. Each function is for a different type of data.

Method signature
/**
* This method allows you to add boolean state to your component.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {boolean} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {('text' | 'checkbox' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[boolean, (newValue: boolean) => void]} The value of the state, and a function to update it.
*/
function useStateBoolean({
name,
initialValue,
inspector,
description,
label,
}: {
name: string
initialValue?: boolean
label?: string
description?: string
inspector?: 'text' | 'checkbox' | 'hidden'
}): readonly [boolean, (newValue: boolean) => void]

/**
* This method allows you to add number state to your component.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {number} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[number, (newValue: number) => void]} The value of the state, and a function to update it.
*/
function useStateNumber({
name,
initialValue,
inspector,
description,
label,
}: {
name: string
initialValue?: number
label?: string
description?: string
inspector?: 'text' | 'hidden'
}): readonly [number, (newValue: number) => void]

/**
* This method allows you to add string state to your component.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {string} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[string, (newValue: string) => void]} The value of the state, and a function to update it.
*/
function useStateString({
name,
initialValue,
inspector,
description,
label,
}: {
name: string
initialValue?: string
label?: string
description?: string
inspector?: 'text' | 'hidden'
}): readonly [string, (newValue: string) => void]

/**
* This method allows you to add enumeration state to your component. This is state that can have a value drawn from a limited set of strings.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {T} enumDefinition An array of string literals describing the possible enum values. The strings must be alphanumeric with no spaces.
* @param {T[number]} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {{[K in T[number]]: string}} [enumLabels] Alternative labels to use for enums when displaying them.
* @param {('segmented' | 'select')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[T[number], (newValue: T[number]) => void]} The value of the state, and a function to update it.
*/
function useStateEnumeration<T extends string[]>({
name,
enumDefinition,
initialValue,
enumLabels,
inspector,
description,
label,
}: {
name: string
initialValue?: T[number]
enumDefinition: T
enumLabels?: {
[K in T[number]]: string
}
inspector?: 'segmented' | 'select' | 'hidden'
description?: string
label?: string
}): readonly [T[number], (newValue: T[number]) => void]

/**
* This method allows you to add serializable object state to your component.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {SerializableObject} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[SerializableObject, (newValue: SerializableObject) => void]} The value of the state, and a function to update it.
*/
function useStateObject({
name,
initialValue,
inspector,
description,
label,
}: {
name: string
initialValue?: SerializableObject
inspector?: 'text' | 'hidden'
description?: string
label?: string
}): readonly [SerializableObject, (newValue: SerializableObject) => void]

/**
* This method allows you to add serializable array state to your component.
* Like any other component in Retool, custom components can have their own state, which you can then edit using the Inspector.
*
* @param {string} name The name of the state used internally, and the label that will be used in the Inspector to identify it.
* This should be an alphanumerical string with no spaces.
* @param {SerializableArray} [initialValue] The initial value for the state when the component is dragged onto the canvas.
* @param {('text' | 'hidden')} [inspector] What kind of Inspector will be used when a builder is editing this state.
* @param {string} [description] What will be displayed in the tooltip of the Inspector for this state.
* @param {string} [label] An override for the label used in the Inspector for this state.
*
* @return {[SerializableArray, (newValue: SerializableArray) => void]} The value of the state, and a function to update it.
*/
function useStateArray({
name,
initialValue,
inspector,
description,
label,
}: {
name: string
initialValue?: SerializableArray
inspector?: 'text' | 'hidden'
description?: string
label?: string
}): readonly [SerializableArray, (newValue: SerializableArray) => void]
Example
export const HelloWorldComponent: FC = () => {
// Allows the builder to specify a "name" property on each component they build with.

const [showBorder, _setShowBorder] = Retool.useStateBoolean({
name: "showBorder",
initialValue: false,
label: "Show Border",
inspector: "checkbox",
});

return (
<div
style={{
border: showBorder ? "1px solid black" : "",
}}
>
Hello!
</div>
);
};

Trigger event handlers

The Retool.useEventCallback method allows you to notify Retool of component events, which can then be used to trigger event handlers within Retool.

Method signature
/**
* Defines an event callback for your component. While building with the component you will be able to create event handlers that are triggered
* whenever this event callback is called.
*
* For example, you could create an event callback which is triggered whenever a user clicks on a button in your component.
*
* The event cannot contain any data or context.
*
* @param {string} name The name of the label that the event callback will be given in the Inspector.
*/
function useEventCallback({ name }: { name: string }): () => void

Example usage
export const ButtonComponent: FC = () => {
// Allows the custom component to inform Retool when a click event
// happens on the component. You can then add event handlers to any ButtonComponent
// that is triggered when onClick is called.
const onClick = Retool.useEventCallback({ name: "click" });

return (
<div>
<button onClick={onClick}> A button </button>
</div>
);
};

Passing data with an event

Data from a custom component can't be included inside an event. To access this kind of data in an event handler, set state in the component before firing the event (in this example text), and then access that state as you normally would in the event handler.

Accessing component data in an event handler
export const CustomInput: FC = () => {
const [text, setText] = Retool.useStateString({
name: "text",
inspector: "hidden",
});

const clickEvent = Retool.useEventCallback({ name: "click" });
return (
<>
<input
type="text"
id="name"
name="name"
value={text}
onChange={(change) => {
setText(change.target.value);
}}
/>
<button onClick={(event) => clickEvent()}>submit</button>
</>
);
};

In your event handler, you can then access the value of {{componentId.text}}.

Event handler that references the text property

Set the component size

Retool.useComponentSettings allows you to set the size of the component when it is first dragged onto the canvas.

Method signature
 /**
* Allows configuration of various settings on your component.
*
* @param {string} defaultWidth Sets the default width in columns of the component when you drag it onto the canvas.
* @param {string} defaultHeight Sets the default height in rows of the component when you drag it onto the canvas.
*/
function useComponentSettings({
defaultWidth,
defaultHeight,
}: {
defaultWidth?: number
defaultHeight?: number
}): void
Example usage
export const HelloWorldComponent: FC = () => {
// Sets the default height and width when HelloWorldComponent is
// dragged onto the canvas.
// dragged onto the canvas. Note that the rows are much shorter than the columns.
Retool.useComponentSettings({
defaultHeight: 50,
defaultWidth: 5,
});

return <div>Hello!</div>;
};