diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index 9568f9530b..9a32991807 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -261,10 +261,10 @@ module.exports = {
// Please change this to your repo.
editUrl: 'https://github.com/ToolJet/Tooljet/blob/develop/docs/',
includeCurrentVersion: true,
- lastVersion: '3.5.0-LTS',
+ lastVersion: '3.16.0-LTS',
versions: {
current: {
- label: '3.11.0-Beta 🚧',
+ label: 'Beta 🚧',
path: 'beta',
banner: 'none',
badge: false
@@ -280,6 +280,10 @@ module.exports = {
"3.5.0-LTS": {
banner: 'none',
badge: false
+ },
+ "3.16.0-LTS": {
+ banner: 'none',
+ badge: false
}
}
},
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/_category_.json b/docs/versioned_docs/version-3.16.0-LTS/actions/_category_.json
new file mode 100644
index 0000000000..f5b2dfe045
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Actions Reference",
+ "position": 7,
+ "collapsed": true
+}
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/close-modal.md b/docs/versioned_docs/version-3.16.0-LTS/actions/close-modal.md
new file mode 100644
index 0000000000..bb772142f9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/close-modal.md
@@ -0,0 +1,19 @@
+---
+id: close-modal
+title: Close modal
+---
+
+Use this action to close the modal that is already shown.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/control-component.md b/docs/versioned_docs/version-3.16.0-LTS/actions/control-component.md
new file mode 100644
index 0000000000..bab10a66bb
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/control-component.md
@@ -0,0 +1,102 @@
+---
+id: control-component
+title: Control component (Component Specific Actions)
+---
+
+Control component action invokes the component specific actions. Component specific actions are the actions that are exclusive actions for a particular component. Component specific actions can be triggered either through the event handlers or from the Run JavaScript code query.
+
+You can find the component specific actions for the specific component in their respective documentation. For example, you can find the component specific actions for the **Bounded Box** component in the [Bounded Box](/docs/widgets/bounded-box) documentation.
+
+
+ Currently, Component specific actions are supported only by the below listed components.
+
+
+
+:::info
+Check out the **[demo](https://youtu.be/JIhSH3YeM3E)** of Component specific actions demonstrated in one of our community call.
+:::
+
+## Using Component Specific Actions
+
+### Set a value for text input component using button's event handler
+
+- Drag a **Text Input** and a **Button** component onto the canvas.
+
+- Go to the **Inspector** on the left sidebar to check the exposed variables available for the `textinput1` component under the `components`. You'll see that the variable `value` is an empty string because the field value of the text input component is empty right now.
+
+
+
+
+
+
+
+- Now enter some value in the text input component and you'll see that the `value` in inspector has been updated.
+
+
+
+
+
+
+
+- Now, click on the button's component handler to open up its properties in the right sidebar and then add a event handler for **On Click** event to trigger **Control Component** action. Select `textinput1` in component dropdown, `Set text` as Action, and in `Text` field enter the text that you want to update in the field value.
+
+
+
+
+
+
+
+- Now when you'll click on the button you'll see that the field value of the text input component has been updated with value that you set.
+
+
+
+
+
+
+
+
+### Clear value of text input component using JavaScript query
+
+- Let's clear the value that we set in the previous section, using Run JavaScript code. Create a new Run JavaScript Code query and call the component and the CSA that component provides.
+
+**Syntax:**
+```js
+await components.textinput1.clear()
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+- Finally, hit the **save and run** query button to fire up the query, and you'll see that the field value of the text input component has been cleared.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/copy-to-clipboard.md b/docs/versioned_docs/version-3.16.0-LTS/actions/copy-to-clipboard.md
new file mode 100644
index 0000000000..81f97c360e
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/copy-to-clipboard.md
@@ -0,0 +1,18 @@
+---
+id: copy-to-clipboard
+title: Copy to clipboard
+---
+
+Use this action to copy the text to the clipboard.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/generate-file.md b/docs/versioned_docs/version-3.16.0-LTS/actions/generate-file.md
new file mode 100644
index 0000000000..5e969b87a8
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/generate-file.md
@@ -0,0 +1,56 @@
+---
+id: generate-file
+title: Generate file
+---
+
+# Generate file
+
+This action allows you to construct files on the fly and let users download it.
+
+## Options
+
+| Option | Description |
+|--------|-------------|
+| Type | Type of file to be generated. Types: `CSV`, `Text` and `PDF` |
+| File name | Name of the file to be generated |
+| Data | Data that will be used to construct the file. Its format will depend on the file type, as specified in the following section |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
+
+:::tip
+Check how to run **[generate file action using RunJS](/docs/how-to/run-actions-from-runjs/#generate-file)**.
+:::
+
+### CSV Data Format
+
+To use the `CSV` file format, the data field should contain an array of objects. ToolJet assumes that the keys in each object are the same and represent the column headers of the CSV file.
+
+Example:
+
+```javascript
+{{
+ [
+ { name: 'John', email: 'john@tooljet.com' },
+ { name: 'Sarah', email: 'sarah@tooljet.com' },
+ ]
+}}
+```
+
+Using the above code snippet will generate a CSV file with the following content:
+
+```csv
+name,email
+John,john@tooljet.com
+Sarah,sarah@tooljet.com
+```
+
+### Text Data Format
+
+To use the `Text` file format, the data field should contain a string.
+
+If you want to generate a text file based on an array of objects, you need to stringify the data before providing it.
+
+For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field.
+
+### PDF data format
+
+The PDF data format supports two types of input: either a `string` or an `array of objects`. When using an array of objects, the resulting PDF will display the data in a tabular format with columns and rows. On the other hand, if a string is provided, the generated PDF will consist of plain text.
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/go-to-app.md b/docs/versioned_docs/version-3.16.0-LTS/actions/go-to-app.md
new file mode 100644
index 0000000000..e38e00468e
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/go-to-app.md
@@ -0,0 +1,18 @@
+---
+id: go-to-app
+title: Go to app
+---
+
+This action allows you to open any released ToolJet application when an event occurs. Only the apps that are released can be opened using this action.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/logout.md b/docs/versioned_docs/version-3.16.0-LTS/actions/logout.md
new file mode 100644
index 0000000000..0e3187471c
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/logout.md
@@ -0,0 +1,18 @@
+---
+id: logout
+title: Logout
+---
+
+This action allows you to log out of the application (ToolJet).
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/open-webpage.md b/docs/versioned_docs/version-3.16.0-LTS/actions/open-webpage.md
new file mode 100644
index 0000000000..749b205eb9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/open-webpage.md
@@ -0,0 +1,18 @@
+---
+id: open-webpage
+title: Open webpage
+---
+
+You can use this action to open a webpage(on a new tab) for any event.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/run-query.md b/docs/versioned_docs/version-3.16.0-LTS/actions/run-query.md
new file mode 100644
index 0000000000..55eb1f16aa
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/run-query.md
@@ -0,0 +1,18 @@
+---
+id: run-query
+title: Run Query
+---
+
+This action allows you to fire queries when an event occurs.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/set-localstorage.md b/docs/versioned_docs/version-3.16.0-LTS/actions/set-localstorage.md
new file mode 100644
index 0000000000..bfa52c0a5e
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/set-localstorage.md
@@ -0,0 +1,58 @@
+---
+id: set-localstorage
+title: Set localStorage
+---
+
+# Set localStorage
+
+This action allows you to specify a `key` and its corresponding `value` to be stored in local storage of the browser. Local storage can be useful in a lot of scenarios. Some of the most common use cases of the local storage includes:
+- Saving form values so that users don't accidentally lose them if they reload the page
+- Storing any kind of data that is not going to be transferred to the database
+
+
+
+## Example: Setting a Component Value Based on Local Storage
+
+1. Add **Text Input**, **Button** and **Text** components to the canvas.
+
+
+
+
+
+
+2. Select the Button, add a new event handler, and add a `Set local storage` action with `key` set to `localtest` and `value` set to `{{components.textinput1.value}}`.
+
+
+
+
+
+ This will set a local storage value with `localtest` as the key and the value entered in the Text Input component as its value.
+
+3. Create a `Run JavaScript code` query, and enter the code below:
+
+ ```js
+ return localStorage.getItem("localtest");
+ ```
+
+
+
+
+ Click on the **Run** button in the Query Panel. This query will fetch the `localtest` local storage variable that we had set earlier.
+
+4. Select the **Text** component. Under its `Text` property, enter `{{queries.runjs1.data}}`. Now, the Text component will display the value returned by the `Run JavaScript code` query - the local variable we had set earlier.
+
+
+
+
+5. Select the Button component. Add a new event handler to it, add a `Run query` action, select `runjs1` as the query, and set a debounce of `300`.
+
+
+
+
+ Now, every time you click on the Button component, it will set the local storage value, and the Text component will display the value set in local storage.
+
+ :::info
+ Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+ :::
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/set-page-var.md b/docs/versioned_docs/version-3.16.0-LTS/actions/set-page-var.md
new file mode 100644
index 0000000000..5c50bab3ee
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/set-page-var.md
@@ -0,0 +1,36 @@
+---
+id: set-page-variable
+title: Set page variable
+---
+
+Page variables are restricted to the page where they are created and cannot be accessed throughout the entire application like regular variables.
+
+Use this action to establish a variable and assign a value to it within the [Multipage Apps](/docs/tutorial/pages).
+
+By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
+
+
+
+
+
+
+
+## Using RunJS query to set page variable
+
+Alternatively, the set page variable action can be triggered via a RunJS query using the following syntax:
+```js
+await actions.setPageVariable('',)
+```
+
+`variablekey` must be provided as a string (enclosed in quotes), while the `variablevalue` does not require quotation marks if it is a numerical value.
+
+
+
+
+
+
+
+:::info
+For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
+:::
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/set-table-page.md b/docs/versioned_docs/version-3.16.0-LTS/actions/set-table-page.md
new file mode 100644
index 0000000000..6c6414650c
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/set-table-page.md
@@ -0,0 +1,24 @@
+---
+id: set-table-page
+title: Set Table Page
+---
+
+Use this action to change the page index in the table widget.
+
+## Options
+
+| Option | Description |
+|--------|-------------|
+| Table | Select table from the dropdown |
+| Page Index | Numerical value for the page index. ex: `{{2}}` |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/set-variable.md b/docs/versioned_docs/version-3.16.0-LTS/actions/set-variable.md
new file mode 100644
index 0000000000..ef611d43ba
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/set-variable.md
@@ -0,0 +1,24 @@
+---
+id: set-variable
+title: Set variable
+---
+
+This action allows you to create a variable and assign a `value` to it.
+
+## Options
+
+| Option | Description |
+|--------|-------------|
+| Key | Name(String) of the variable through which you can access the value |
+| Value | A value can be a string, number, boolean expression, array, or object |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/show-alert.md b/docs/versioned_docs/version-3.16.0-LTS/actions/show-alert.md
new file mode 100644
index 0000000000..f92ec12cd7
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/show-alert.md
@@ -0,0 +1,23 @@
+---
+id: show-alert
+title: Show alert
+---
+
+This action allows you to display an alert message.
+
+You can set a custom **message** for the alert and choose a particular alert type.
+
+There are 4 types of alert messages - **Info**, **Success**, **Warning**, and **Error**.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/show-modal.md b/docs/versioned_docs/version-3.16.0-LTS/actions/show-modal.md
new file mode 100644
index 0000000000..10f5d9ab17
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/show-modal.md
@@ -0,0 +1,18 @@
+---
+id: show-modal
+title: Show modal
+---
+
+Use this action to show the modal for an event.
+
+Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/switch-page.md b/docs/versioned_docs/version-3.16.0-LTS/actions/switch-page.md
new file mode 100644
index 0000000000..2438fb45e7
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/switch-page.md
@@ -0,0 +1,55 @@
+---
+id: switch-page
+title: Switch Page
+---
+
+Utilize this action with various event handler to transition to a different page within the [Multipage App](/docs/tutorial/pages).
+
+By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
+
+
+
+
+
+
+
+## Query Params
+
+Query parameters can be passed through action such as `Switch Page`. The parameters are appended to the end of the application URL and are preceded by a question mark (`?`).
+
+Query parameters are composed of key-value pairs, where the `key` and `value` are separated by an equals sign (`=`). Multiple query parameters can be included by clicking on the `+` button.
+
+
+
+
+
+
+
+In the above screenshot, we have provided the `username` as the key and the value is `{{globals.currentUser.email}}` which gets the email of the signed in user dynamically. When the button is clicked to trigger the `Switch Page` event handler attached to it then the URL on the switched page will have the parameters.
+
+They are commonly used to provide additional information to the server or to modify the behavior of a web page. They can be used for filtering search results, pagination, sorting, and various other purposes.
+
+
+
+
+
+
+
+## Using RunJS query to switch page
+
+Alternatively, the switch page action can be activated via a RunJS query using the following syntax:
+```js
+await actions.switchPage('')
+```
+
+:::info
+For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
+:::
+
+### Switch page with query params
+
+The switch page action can also be triggered along with query parameters using the following syntax:
+
+```js
+actions.switchPage('', [['param1', 'value1'], ['param2', 'value2']])
+```
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-page-var.md b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-page-var.md
new file mode 100644
index 0000000000..3adef08f49
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-page-var.md
@@ -0,0 +1,20 @@
+---
+id: unset-all-page-var
+title: Unset All Page Variables
+---
+
+Using this action you can unset all the page level variables at once.
+
+## Options
+
+|
Option
| Description |
+|:-------|:------------|
+| Run Only if | Add a condition. |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. For example: `300` |
+
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-var.md b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-var.md
new file mode 100644
index 0000000000..b14c42aa17
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-all-var.md
@@ -0,0 +1,20 @@
+---
+id: unset-all-var
+title: Unset All Variables
+---
+
+Using this action you can unset all the app level variables at once.
+
+## Options
+
+|
Option
| Description |
+|:-------|:------------|
+| Run Only if | Add a condition. |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. For example: `300` |
+
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/unset-page-var.md b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-page-var.md
new file mode 100644
index 0000000000..933721c12d
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-page-var.md
@@ -0,0 +1,27 @@
+---
+id: unset-page-variable
+title: Unset page variable
+---
+
+Utilize this action to clear a variable that was established using the [set page variable action](/docs/actions/set-page-variable).
+
+By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
+
+
+
+
+
+
+
+## Using RunJS query to unset variable
+
+Alternatively, the unset page variable action can be triggered via a RunJS query using the following syntax:
+```js
+await actions.unsetPageVariable('')
+```
+
+`variablename` is the key of the variable that was provided while creating the variable.
+
+:::info
+For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
+:::
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/actions/unset-variable.md b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-variable.md
new file mode 100644
index 0000000000..7da5a78161
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/actions/unset-variable.md
@@ -0,0 +1,23 @@
+---
+id: unset-variable
+title: Unset variable
+---
+
+This action allows you to remove the variable that was created using the set variable action.
+
+## Options
+
+| Option | Description |
+|--------|-------------|
+| Key | Name(String) of the variable through which you can access the value |
+| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
+
+:::info
+You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
+:::
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/anti-patterns.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/anti-patterns.md
new file mode 100644
index 0000000000..096f10ad1f
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/anti-patterns.md
@@ -0,0 +1,131 @@
+---
+id: anti-patterns
+title: Anti-Patterns to Avoid
+---
+
+When building applications with ToolJet, it's essential to follow best practices to ensure your apps are efficient, maintainable, and provide a smooth user experience. This documentation outlines common anti-patterns to avoid while using ToolJet and offers solutions to optimize your applications.
+
+---
+
+## 1. Unmanaged Component Naming
+
+- **Anti-Pattern**: Using default or non-descriptive names for components.
+- **Solution**: **Rename all components with meaningful names to make the apps more manageable as they grow.**
+- **Reason**: Descriptive names improve readability, making it easier for you and others to understand and maintain the app's structure.
+
+---
+
+## 2. Exceeding Component Limits
+
+- **Anti-Pattern**: Having more than 2,500 components in a single app.
+- **Solution**: **Limit each app to a maximum of 2,500 components.**
+- **Reason**: Exceeding this number can slow down the app builder and live apps, impacting both development speed and user experience.
+
+---
+
+## 3. Client-Side Operations for Large Data Sets
+
+- **Anti-Pattern**: Handling large data sets with client-side operations on the Table component.
+- **Solution**: **Implement [server-side operations](/docs/widgets/table/serverside-operations/overview) for handling large data sets.**
+- **Reason**: Server-side operations reduces the amount of data loaded at once, improving load times and performance.
+
+---
+
+## 4. Simultaneous Execution of Multiple JavaScript Queries
+
+- **Anti-Pattern**: Triggering a large amount of JavaScript queries simultaneously through a single event. For example, using an event to trigger a **Run JavaScript code** query that contains code to execute 15-20 other **Run JavaScript code** queries within the application.
+- **Solution**: **Limit the number of simultaneous JavaScript queries triggered by a single event.**
+- **Reason**: Triggering numerous Run JavaScript queries at the same time can significantly degrade browser performance as it each JavaScript query creates **[new execution environment](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth#javascript_execution_contexts)** within the browser. JavaScript in browsers runs on a single main thread. When multiple scripts are executed concurrently, they compete for execution time on this thread.
+
+---
+
+## 5. Storing Base64 Data in Variables
+
+- **Anti-Pattern**: Capturing and storing Base64 data directly in variables.
+- **Solution**: **Store large data, like base64 images, in a database and retrieve it as needed.**
+- **Reason**: Storing Base64 data in variables can consume significant memory and slow down the app. Retrieving data from a database as needed optimizes performance.
+
+---
+
+## 6. Loading All Tabs Simultaneously
+
+- **Anti-Pattern**: Loading all items in the Tab component at once when there are numerous tabs.
+- **Solution**: **Enable the “Render only active tabs” option.**
+- **Reason**: This prevents unnecessary loading of inactive tabs, reducing initial load times and improving performance.
+
+---
+
+## 7. Excessive Number of Pages in an App
+
+- **Anti-Pattern**: Including too many pages within a single app.
+- **Solution**: **Limit the number of pages per app to maintain optimal performance.**
+- **Reason**: An excessive number of pages can slow down the app and make it difficult to manage. Organize content efficiently and consider splitting the app if necessary.
+
+---
+
+## 8. Using Non-Blocking Commands in JavaScript for Synchronous Operations
+
+- **Anti-Pattern**: Using non-blocking commands like `Promise.all` and `setTimeout` in the **Run JavaScript code** query when an accurate isLoading state is needed.
+- **Solution**: **Avoid non-blocking operations in JavaScript Queries if you require an accurate isLoading status. Ensure your code is synchronous within the Run JavaScript code query.**
+- **Reason**: Non-blocking operations can cause **Run JavaScript code** query to exit before these commands complete, leading to an incorrect isLoading status and potentially confusing users.
+
+---
+
+## 9. Triggering Unnecessary Queries on Page Load
+
+- **Anti-Pattern**: Triggering all queries on page load, regardless of their necessity.
+- **Solution**: **For multi-page apps, only trigger queries on page load that are needed for the specific page.**
+- **Reason**: Loading unnecessary data consumes resources and slows down page load times. Optimizing queries enhances performance.
+
+---
+
+## 10. Using Actions inside Loop Functions
+- **Anti-Pattern**: Using actions inside loop functions.
+
+Example:
+You have a Table displaying data from `{{page.variables.data}}` and a **Save Changes** button that updates the data. When users edit rows and click **Save Changes**, you might initially implement the update like this:
+
+```javascript
+const data = page.variables.data;
+Object.values(components.table1.dataUpdates).forEach(ele => {
+ data[ele.id] = ele;
+ actions.setPageVariable("data", data);
+});
+```
+
+The setPageVariable action is executed inside the loop for each row update. This causes the table to re-render every time the variable is updated, leading to significant performance degradation, especially when multiple rows or cells are updated simultaneously.
+
+- **Solution**: **Modify your code to update the page variable once after all changes are processed**:
+
+```javascript
+const data = page.variables.data;
+Object.values(components.table1.dataUpdates).forEach(ele => {
+ data[ele.id] = ele;
+});
+actions.setPageVariable("data", data);
+```
+
+- **Reason**: By updating the variable after the loop completes, the table re-renders only once. This reduces unnecessary processing and significantly improves performance when handling multiple updates.
+
+---
+
+## 11. Direct Mutation of Data
+
+- **Anti-Pattern**: Directly mutating data structures through JavaScript code, such as using `queries.getEmployees.data = []`.
+- **Solution**: Always use ToolJet's built in **[actions](/docs/how-to/run-actions-from-runjs/)** to manipulate data.
+- **Reason**: Direct mutation of data can lead to unexpected bugs and make debugging more complex.
+
+---
+
+## 12. Naming Component/Query with Hyphen or Space
+
+- **Anti-Pattern**: Naming components or queries with hyphens or spaces in between, such as `run-py1` or `my query`.
+- **Solution**: **Use names without hyphens or spaces**, or reference them using bracket notation (e.g., `{{queries['run-py1'].isLoading}}`).
+- **Reason**: Hyphens and spaces can cause syntax issues. Using bracket notation or avoiding these characters ensures consistency and prevents errors in query or component references.
+
+---
+
+## Conclusion
+
+Avoiding these anti-patterns when using ToolJet ensures that your applications are efficient, responsive, and maintainable. By following these best practices, you can enhance user experience and simplify app management. Always consider the impact of your development choices on both performance and scalability.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/canvas.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/canvas.md
new file mode 100644
index 0000000000..116a033ee3
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/canvas.md
@@ -0,0 +1,69 @@
+---
+id: canvas
+title: Canvas and Layout
+---
+
+**Canvas** is the main area in the app-builder where you build your application and design the user interface.
+
+
+
+## Customizing Canvas
+
+Through Global Settings, you can customize the following properties of the **Canvas**:
+
+- **Max width of canvas**: Defines the maximum width of the canvas, which can be set in pixels or as a percentage of the screen size. The height of the canvas expands automatically as more components are added.
+- **Canvas background**: Sets the background color of the canvas. This can also be controlled dynamically by clicking on **fx** and entering a logical expression.
+- **App mode**: You can choose from three theme modes
+ - **Auto**: Adapts to the browser's theme settings or allows users to switch between light and dark modes.
+ - **Light**: Keeps the app in light mode, users cannot switch to dark mode.
+ - **Dark**: Keeps the app in dark mode, users cannot switch to light mode.
+
+
+
+## Building the User Interface
+
+To build the user interface, components can be dragged from the [Components Library](/docs/beta/app-builder/building-ui/component-library) on the right. Use the Component Handle to reposition a component. A component can be resized from any of its edges or corners.
+
+
+
+### Grid, Snapping and Markers
+
+ToolJet's Canvas provides a grid background, smart snapping, and visual markers to support precise alignment and positioning of components. Components automatically snap to grid lines and nearby elements, reducing the need for manual adjustments. Each cell on the canvas grid has a fixed height of 10 pixels. The width of each cell adjusts based on the screen size.
+
+
+
+## Creating Layout
+
+In ToolJet, components can be grouped using a Layout component such as a **[Container](/docs/widgets/container)** or a **[Form](/docs/widgets/form)**. You can drag and drop the relevant components into the layout components on the canvas to create a section.
+
+
+
+## Managing Components on Canvas
+
+#### Select and Move Multiple Components
+
+You can select multiple components by clicking and dragging over them, or by clicking individually while holding the Shift key. Once selected, all components can be moved together as a group.
+
+#### Copy Components
+
+Components on the canvas can be copied using **Cmd/Ctrl + C**.
+
+
+
+#### Paste Components
+
+Copied components can be pasted onto the canvas using **Cmd/Ctrl + V**.
+
+
+
+#### Clone Components
+
+Components on the canvas can be cloned using **Cmd/Ctrl + D**. Unlike copy and paste, cloning creates a duplicate of the selected component instantly.
+
+
+
+
+
+:::note
+For additional shortcuts, refer to the [Keyboard Shortcuts Guide](/docs/tutorial/keyboard-shortcuts).
+:::
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-library.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-library.md
new file mode 100644
index 0000000000..6ce18fbb1d
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-library.md
@@ -0,0 +1,28 @@
+---
+id: component-library
+title: Component Library
+---
+
+**Components** are the building blocks of ToolJet applications, used to create interactive user interfaces. The **Component Library**, located on the right, contains all [available components](#available-components).
+
+Components can be added to the canvas by dragging and dropping them onto the canvas at your desired location.
+
+
+
+## Available Components
+
+| Category | Components |
+|:----------|:------------|
+| **Buttons** | [Button](/docs/widgets/button), [Button Group](/docs/widgets/button-group) |
+| **Data** | [Table](/docs/widgets/table/), [Chart](/docs/widgets/chart/)|
+| **Layouts** | [Form](/docs/widgets/form), [Modal](/docs/widgets/modal), [Container](/docs/widgets/container), [Tabs](/docs/widgets/tabs), [ListView](/docs/widgets/listview), [Kanban](/docs/widgets/kanban), [Calendar](/docs/widgets/calendar) |
+| **Text Inputs** | [Text Input](/docs/widgets/text-input), [Text Area](/docs/widgets/textarea), [Email Input](/docs/widgets/email-input), [Password Input](/docs/widgets/password-input), [Rich Text Editor](/docs/widgets/rich-text-editor) |
+| **Number Inputs** | [Number Input](/docs/widgets/number-input), [Phone Input](/docs/widgets/phone-input), [Currency Input](/docs/widgets/currency-input), [Range Slider](/docs/widgets/range-slider), [Star Rating](/docs/widgets/star-rating) |
+| **Select Inputs** | [Dropdown](/docs/widgets/dropdown), [Multi-Select](/docs/widgets/multiselect), [Toggle Switch](/docs/widgets/toggle-switch-v2), [Radio Button](/docs/widgets/radio-button), [Checkbox](/docs/widgets/checkbox), [Tree Select](/docs/widgets/tree-select) |
+| **Date and Time Inputs** | [Date-range Picker](/docs/widgets/date-range-picker), [Date Picker](/docs/widgets/date-picker-v2), [Time Picker](/docs/widgets/time-picker), [Date-Time Picker](/docs/widgets/datetime-picker-v2) |
+| **Navigation** | [Link](/docs/widgets/link), [Pagination](/docs/widgets/pagination), [Steps](/docs/widgets/steps) |
+| **Media** | [Icon](/docs/widgets/icon), [Image](/docs/widgets/image), [SVG Image](/docs/widgets/svg-image), [PDF](/docs/widgets/pdf), [Map](/docs/widgets/map) |
+| **Presentation** | [Text](/docs/widgets/text), [Tags](/docs/widgets/tags), [Circular Progressbar](/docs/widgets/circular-progress-bar), [Timeline](/docs/widgets/timeline), [Divider](/docs/widgets/divider), [Vertical Divider](/docs/widgets/vertical-divider), [Spinner](/docs/widgets/spinner), [Statistics](/docs/widgets/statistics), [Timer](/docs/widgets/timer) |
+| **Custom** | [Custom Component](/docs/widgets/custom-component), [HTML Viewer](/docs/widgets/html), [iFrame](/docs/widgets/iframe) |
+| **Miscellaneous** | [Filepicker](/docs/widgets/file-picker), [Code Editor](/docs/widgets/code-editor), [Color Picker](/docs/widgets/color-picker), [Bounded Box](/docs/widgets/bounded-box), [QR Scanner](/docs/widgets/qr-scanner) |
+| **Legacy** | [Modal](/docs/widgets/modal), [Datetime Picker](/docs/widgets/datepicker/), [Radio Button](/docs/widgets/radio-button), [Toggle Switch](/docs/widgets/toggle-switch), [Dropdown](/docs/2.50.0-LTS/widgets/dropdown), [Multiselect](/docs/widgets/multiselect) |
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-properties.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-properties.md
new file mode 100644
index 0000000000..04740d55ce
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-properties.md
@@ -0,0 +1,43 @@
+---
+id: component-properties
+title: Component Properties
+---
+
+**Component properties** define the appearance, behavior, and interactivity of UI components in ToolJet. It also allows you to configure component-level permissions, enabling only selected users or user groups to interact with the component.
+
+Each component includes a unique set of properties based on its functionality. Here’s an overview of common types of configurable properties:
+
+- **Labels and Data Fields**: For input components, you can configure the label, add placeholders, default values, define validation rules, etc.
+- **Data**: Populate components with static values or dynamic data through queries.
+- **Events**: Events are actions or triggers that respond to user interactions or specific conditions in your application. They let you define custom logic (like running a query, navigating to a page, or showing a toast) in response to user activity or application changes — without writing backend code.
+- **Styles**: Define visual attributes like colors, spacing, alignment, and border radius to adjust how the component appears.
+- **State**: Control component states such as loading, visibility, or whether the component is disabled. You can toggle these manually or control them using logical expressions.
+- **Device**: Configure whether the component should be visible on specific devices, such as mobile or desktop.
+
+
+
+These are just a few commonly used property types. For detailed information on any specific component and its properties, refer to their individual documentation.
+
+## Component Level Permissions
+
+You can configure component-level permissions to allow only selected end users or user groups to interact with the component. The component will not render at all for users who don’t have access.
+
+Suppose you're building an app to manage customer license details. Sales representatives should be able to create, update, and delete customer information. Meanwhile, Product, Marketing, and Customer Success teams should only view this data. To enforce this, you can configure component-level permissions to hide the Edit and Delete buttons from non-sales users. These buttons won’t render at all for users without access.
+
+### Configuring Component Level Permission
+
+Follow these steps to configure component level permission:
+
+**Role Required**: Admin or Builder
+
+1. Select the component, then click the kebab menu (three dots) next to the component name in the properties panel.
+
+2. Select **Component permission**.
+
+3. Select the **Type**:
+ - **All users with access to the app**: Grants access to all users who can access the application.
+ - **Users**: Select specific users from the dropdown. Note: These users must already have access to the application.
+ - **User groups**: Restricts access to members of selected user groups. Note: The selected user groups must have access to the application.
+
+
+**Note**: If a component's permissions have been configured by an admin and the builder is not included in the allowed users or groups, the builder will not be able to modify the component’s permissions.
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-state.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-state.md
new file mode 100644
index 0000000000..f8523301f3
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/component-state.md
@@ -0,0 +1,36 @@
+---
+id: component-state
+title: Component State
+---
+
+Every ToolJet component maintains a state—a set of values representing its current data and configuration. This state can be accessed through exposed variables, which allow components to interact with other parts of the application. For example, the value entered into a text input component can be passed to a query to fetch data from the database.
+
+Each component has a unique set of exposed variables based on its functionality — for example, a **Table** component exposes `selectedRow`, a checkbox exposes `isChecked`, and so on.
+
+Component states in ToolJet are dynamic and can be modified at runtime using built-in functions called [Component-Specific Actions (CSAs)](/docs/beta/app-builder/events/use-case/csa), such as `reset()`, `setValue()`, and `setVisibility()`. These actions let you trigger logic in response to user interactions.
+
+Component states can be used across the application to build interactive and reactive experiences:
+- In queries — to send user inputs or component values as parameters.
+- In other components — to conditionally display, update, or interact with components.
+
+## Available Component States
+
+In the App-Builder, you can view all available component states using the Inspector located in the left sidebar. For more details, refer to the [Inspector](/docs/beta/app-builder/debugging/inspector) guide.
+
+
+
+- Open the Components dropdown inside the Inspector.
+- Select the component whose state you want to inspect.
+- A secondary dropdown will appear showing all the available states.
+
+You can also copy the state value or its path, which can be used to access it from another component or query. When you hover over a state in the Inspector, two icons appear — one for copying the path and one for copying the value.
+
+
+
+## Accessing Component State
+
+You can access a component’s state using the following syntax:
+`{{components..}}`
+
+Example: `{{components.numberinput1.value}}` - This will fetch the value entered by the user in the **numberinput1** component.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/pages.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/pages.md
new file mode 100644
index 0000000000..96a7a985e9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/building-ui/pages.md
@@ -0,0 +1,136 @@
+---
+id: pages
+title: Pages and Navigation
+---
+
+ToolJet allows the creation of multi-page applications, helping you break your application into different sections. Instead of building everything on a single screen, you can create separate pages to organize different functionalities or enable navigation within your application.
+
+You can add the following items to the navigation menu in ToolJet:
+
+1. **New Page**: Create new pages to support multi-page applications and organize functionalities more effectively.
+2. **Web Pages**: Add external URLs to the navigation menu to redirect users to specific webpages.
+3. **ToolJet Application**: Link to other ToolJet applications directly from the nav menu. Note: The application must be a released app within the same workspace.
+4. **Nav Group**: Group related navigation items together to simplify navigation in complex applications. For example, all admin-related items can go under one group, and user-related items under another.
+
+This guide discusses how pages and navigation menu work, how to create new navigation items manage them.
+
+## Page Properties
+
+Each page in ToolJet comes with properties that define its identity and behavior. These properties help in organizing, referencing, and securing pages within your application.
+
+
+
+### Name
+
+A display name for the page, shown in the application's navigation menu. It is also used to reference the page within the ToolJet application. You can optionally add an icon to make the page easier to identify in the menu. The page name and icon can be updated using the kebab menu (three dots) next to the page name and then going to edit page details.
+
+
+
+### Handle
+
+The page handle is a unique identifier used to generate a shareable URL for the page. It is appended as a slug to the end of your application URL. By default, it's auto-generated from the page name. You can change it manually from edit page details option.
+
+### Home Page
+
+The home page is the default landing page when the app launches. Only one page can be designated as the home page in your application. It cannot be deleted, disabled, or hidden from the page menu. A page can be marked as the home page using the kebab menu (three dots) next to the page name or by going to edit page details option.
+
+### Permissions
+
+Page permissions control who can access a particular page. You can choose to:
+
+- Allow access to all users with application access
+- Restrict access to selected users
+- Restrict access to selected [user groups](/docs/user-management/role-based-access/user-roles)
+
+To configure page permissions, click the kebab menu (three dots) next to the page name, select Page permission, and select a permission option from the popup.
+
+### Disable Page
+
+**Disable Page** allows you to disable a page, making it inaccessible in the released application. A page marked as Home cannot be disabled.
+
+### Hide Page on Navigation Menu
+
+You can hide a page from the navigation menu in the released application. However, hidden pages can still be accessed using the Switch Page action or by navigating directly to the page URL. Pages set as Home cannot be hidden.
+
+## Header and Navigation Menu
+
+### App Header
+
+The app header section allows you to control what is displayed in the application’s header.
+
+
+
+- **Show app header**: Toggle this on to display the app header.
+- **Show logo**: Toggle this on to display the application logo. You can update the logo from the [white labeling](/docs/tj-setup/org-branding/white-labeling/) settings.
+- **Title**: Set a title for the application. This will be displayed in the app header.
+
+### Navigation Menu
+
+The **Navigation Menu** lets users navigate between pages, external web pages and other ToolJet applications in your application. You can customize how it looks and works, or even hide certain pages from it.
+
+#### Show Navigation Menu
+Toggle this on to display the navigation menu. When disabled, no navigation menu will be displayed, but users will be still able to navigate using events and page urls.
+
+#### Position
+Choose whether to display the navigation menu at the top or on the side of the application.
+
+**Top Navigation Menu**
+
+
+**Side Navigation Menu**
+
+
+#### Style
+
+**Top Navigation Menu**
+Choose the display style for the top navigation menu: Text only or Text + Icon. The top navigation menu cannot be collapsed.
+
+**Side Navigation Menu**
+Choose display styles from: Text only, Icon only, or Text + Icon. The side navigation menu also supports a collapsible layout.
+
+## Event Handlers
+
+The Page Event Handler lets you trigger actions automatically when a page loads. Use it to prepare data, set default values, or run any required logic.
+
+For example, it can run a query to fetch the latest data from the database and populate it in a table component. This ensures the page is ready with up-to-date information whenever it is loaded.
+
+
+
+## Exposed Variables
+
+Exposed variables are values from a page that can be accessed throughout the application. These include default page-level values like page name, page ID, and page handle. In addition to the default ones, custom page variables can also be defined and accessed as exposed variables.
+
+| Variable | Description | How To Access |
+| ----------- | ----------- | ------------- |
+| handle | Represents the slug of the page within the app. It is automatically set when a page is created, but can be [renamed](#handle) from the page options. | `{{page.handle}}`|
+| name | Indicates the name of the page. | `{{page.name}}` |
+| id | Each page in the ToolJet application receives a unique identifier upon creation. | `{{page.id}}` |
+| variables | Variables object contains all the variables created for a specific page using the [Set Page Variable](/docs/actions/set-page-variable) action. | `{{page.variables.}}`, where `` refers to the variable name. |
+
+## Manage Navigation Item
+
+### New Page
+
+You can add a new page to organize the application navigation or to separate different parts of your app. To add a new page, click on the **+ New page** button at the bottom of the Pages and menu panel. Enter the page name and press enter to create the page.
+
+
+
+### Web Page
+
+To link an external web page to the navigation menu, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav item with URL**.
+Enter a name and provide the URL. You can also choose whether to open the web page in a new tab or in the same tab, and optionally select an icon for the navigation item.
+
+
+
+### ToolJet App
+
+To add a ToolJet application to the navigation menu, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav item ToolJet app**.
+Enter a name and select the application from the dropdown. Only the release application from the same workspace will appear in the dropdown. You can also choose whether to open the application in a new tab or in the same tab, and optionally select an icon for the navigation item.
+
+
+
+### Nav Group
+
+Related navigation item can be grouped together using the nav group. To add a new nav group, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav group**. Enter the group name and press enter to create the group. You can then drag items into the group folder. You can also add an icon to the group for better visual identification.
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/accessing-query-results.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/accessing-query-results.md
new file mode 100644
index 0000000000..2f366ae757
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/accessing-query-results.md
@@ -0,0 +1,24 @@
+---
+id: accessing-query-results
+title: Accessing Query Results
+---
+
+Once your query is created and executed, the next step is to actually use the data, whether you’re displaying it in a table, populating dropdowns, or using it in logic for another query. Let’s walk through how you can work with query results.
+
+To better understand what your query is returning, use the Inspector panel. Click on the Inspect button, select your query from the query dropdown. You'll find the following keys:
+- **data**: The processed response returned from the query. This is the data you typically bind to components.
+- **rawData**: The original API response. It's useful for debugging.
+- **isLoading**: A boolean indicating whether the query is currently running. Great for showing loaders or disabling buttons during fetches.
+
+
+
+
+
+You can pass query results to a component by using the `{{ }}` syntax. For example, if you have a query named *getEmployees*, you can pass its data to a Table component by setting the table's data property to `{{queries.getEmployees.data}}`. Learn more about binding queries to components [here](/docs/app-builder/connecting-with-data-sources/binding-data-to-components).
+
+### Quick Actions
+In the Inspector panel, when you hover over a property like data, you’ll see two icons. These icons allow you to quickly copy either the path or value of that property. Here’s what they do:
+1. Copy Path - Copies the full path (e.g., `{{queries.getEmployees.data}}`) so you can reference it directly into component fields.
+2. Copy Value - Copies the actual data returned, useful when inspecting values or mocking responses.
+
+These icons are available for every property, making it easier to wire up your data to components.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/binding-data-to-components.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/binding-data-to-components.md
new file mode 100644
index 0000000000..b78ce125fd
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/binding-data-to-components.md
@@ -0,0 +1,52 @@
+---
+id: binding-data-to-components
+title: Binding Data to Components
+---
+
+In this section, you’ll learn how to connect and bind data to components within ToolJet, whether the data comes from a data source or other components in your app.
+
+You can display data from your data source queries in the components using the `{{ }}` syntax. For instance, you can pass data to a **Table** component's Data property using the following format: `{{ queries..data }}`
+
+For example, you are working on an Employee Directory app where you want to show all employees in a table. If you have a query named *listEmployees* that returns an array of employee objects, you can pass its data to a **Table** component by setting the table's data property to `{{queries.listEmployees.data}}`.
+
+
+
+
+ToolJet also supports JavaScript expressions inside `{{ }}`, allowing you to dynamically transform data before display. Here are a few use cases:
+
+## Use cases
+### Filtering Data
+If you want to show only employees from the ‘Engineering’ department:
+
+```js
+{{ queries.listEmployees.data
+ .filter(employee => employee.department === 'Engineering') }}
+```
+### Map Data
+
+If you want to show only employee names in a dropdown:
+
+```js
+{{ queries.listEmployees.data
+ .map(employee => employee.name) }}
+```
+
+### Conditional Rendering
+
+If you want to show a greeting if an employee is selected in a table:
+
+```js
+{{ components.table1.selectedRow ? `Hello, ${components.table1.selectedRow.name}` : "" }}
+```
+
+### Chaining Expressions
+
+You can also chain multiple JavaScript methods for more complex transformations. For example, filtering and then mapping:
+
+```js
+{{ queries.listEmployees.data
+ .filter(emp => emp.department === 'Engineering')
+ .map(emp => emp.name.toUpperCase()) }}
+```
+
+These expressions give you control over how data is displayed and interacted with inside your ToolJet applications.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/creating-managing-queries.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/creating-managing-queries.md
new file mode 100644
index 0000000000..c18b338667
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/creating-managing-queries.md
@@ -0,0 +1,110 @@
+---
+id: creating-managing-queries
+title: Creating and Managing Queries
+---
+
+A query is a way to interact with your **[data sources](/docs/data-sources/overview)** and acts as the link between your app’s UI and your data. Queries connect your app to configured data sources such as SQL, NoSQL, vector databases, APIs, spreadsheets, and cloud services. Whether it’s retrieving records from your MongoDB collection or updating data in a SQL database, you can use queries to interact with them.
+
+Queries are created in the Query Panel, located at the bottom of the App Builder, where you can either use a visual form-based builder or write code/SQL manually.
+
+
+
+
+## Creating a New Query
+
+- Click on the **+** button in the Query Panel to open a menu listing the available data sources or you can add a new data source by clicking on **+ Add new Data Source** button.
+- Select the desired data source.
+
+
+
+## Configuring the Query
+
+The interface for configuring queries depends on the type of data source. If you are using any SQL data source, you can configure your query using either GUI mode or SQL mode. Rest of the data sources can be configured using form-based GUI.
+
+### GUI mode
+
+- For the Postgres data source, when using GUI mode (as shown in the image below), you’ll need to select the operations you want to perform and then fill out the required fields.
+
+
+- In this example using the AWS S3 data source, you can perform the **Upload object** operation to upload a file to an S3 bucket. You’ll need to provide details such as the bucket name, key, and other relevant parameters based on the selected operation.
+
+
+
+
+### SQL mode
+
+For data sources such as MYSQL, PostgreSQL or SQL Server, you can choose SQL mode where you can write the SQL query to perform your desired operation.
+
+
+
+
+
+
+## Custom Parameters
+You often need a query to fetch different data based on user input, component state, or other logic. Custom parameters allow you to pass dynamic values into a query, making it reusable without hard-coding values.
+
+Let's say you have a query that fetches employee details based on department. Instead of creating a separate query for each department, you can define a parameter like `departmentName`, and use it to filter results dynamically.
+
+To add parameters, simply click the **+ Add** button next to the Parameters label in the query editor.
+
+For each parameter, you need to specify:
+- **Name**: The identifier for the parameter.
+- **Default value**: This value can be a constant string, number, or object.
+
+**Syntax for utilizing the parameter:** Employ `parameters.` in your query. It's important to note that parameters can only be utilized within the specific query where they are defined.
+
+Learn more about **[Using Custom Parameters](/docs/how-to/use-custom-parameters)**.
+
+
+
+## Preview and Run
+
+Before connecting a query to your app’s UI, use the Preview button to check what it returns. This is especially useful when working with external APIs or complex SQL. You can inspect the raw or JSON response, debug any issues, and make sure the data matches what your components need.
+
+Once things look good, use the Run button to execute the query and verify how it interacts with your components and other queries.
+
+## Query Level Permission
+
+You can configure query-level permissions to allow only selected end users or user groups to run the query.
+
+### Configuring Query Level Permission
+
+Follow these steps to configure query level permission:
+
+**Role Required**: Admin or Builder
+
+1. Select the query, then click the kebab menu (three dots) next to the query name on the query panel.
+
+2. Select **Query permission**.
+
+3. Select the **Type**:
+ - **All users with access to the app**: Grants access to all users who can access the application.
+ - **Users**: Select specific users from the dropdown. Note: These users must already have access to the application.
+ - **User groups**: Restricts access to members of selected user groups. Note: The selected user groups must have access to the application.
+
+
+**Note**: If a query's permissions have been configured by an admin and the builder is not included in the allowed users or groups, the builder will not be able to run or modify the query or its permissions.
+
+## Triggers
+
+Triggers allow you to control when and how a query executes within your application. You can find them under the **Settings** tab in the query editor. Following are the triggers available:
+
+### Run This Query on Application Load
+
+You can use this when you want data to be available as soon as the page loads, like auto-fetching a user’s dashboard data or populating dropdown options without requiring user input.
+
+### Request Confirmation Before Running Query
+For actions that modify or delete data, enable this to prompt users for confirmation. It acts as a safeguard against accidental clicks that could alter critical records.
+
+
+### Show Notification on Success
+Let users know when actions are completed successfully. This improves UX by giving real-time feedback. You can customize the message and how long it stays visible.
+
+
+### Retry on Network Errors
+This setting is only available for REST API queries. Here, you get an option to automatically retry REST API requests in case of certain network errors or specific HTTP status codes. By default, it retries a failed API request up to 3 times before marking it as failed. Refer to [REST API Documentation](/docs/data-sources/restapi/querying-rest-api/#retry-on-network-errors) for more details.
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/transforming-data.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/transforming-data.md
new file mode 100644
index 0000000000..ef5d8b84c1
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/connecting-with-data-sources/transforming-data.md
@@ -0,0 +1,64 @@
+---
+id: transforming-data
+title: Transforming Data
+---
+
+Transformations help you clean up your data before passing it to UI components. While building applications, the raw data you fetch from an API or database often needs customization before displaying it in the components. You might need to:
+
+- Convert raw ISO timestamps into DD/MM/YYYY.
+- Flatten deeply nested objects for use in tables or dropdowns.
+- Convert currency, distance, or temperature values before display.
+- Adjust field names to match component expectations.
+
+That’s where data transformations come in. It helps you to shape your backend data into a frontend-friendly format, keeping your UI logic simple and your app easier to maintain.
+
+You can write transformation code in JavaScript or Python. Let’s say you’re building an employee management dashboard, and your getEmployees API returns a lot of extra data:
+
+```json
+[
+ {
+ "id": 1,
+ "name": "John Doe",
+ "email": "john@example.com",
+ "phone_number": "+91876543210",
+ "department_id": 1,
+ "salary": "$50k"
+ },
+ { ... }
+]
+```
+
+But in your table, you only want to display *id*, *name*, *designation* fields. Instead of changing the API or manually filtering inside every component, you can transform the data once, at the query level.
+
+## Using JavaScript
+
+Head to the Transformations tab of your query and write your javascript code:
+
+```javascript
+return data.map(item => ({
+ id: item.id,
+ name: item.name,
+ designation: item.designation
+}));
+```
+
+This ensures every time the query runs, your components receive exactly the shape of data they need.
+
+
+
+## Using Python
+
+If you’re more comfortable with Python, just switch the language in the transformation tab and use a similar approach:
+
+```python
+[
+ {"id":item['id'],
+ "name": item['name'],
+ "designation": item['designation']
+ } for item in data
+]
+```
+
+
+
+Transformations provide you with an easy way to adjust your data before using it in your applications.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/constants-secrets.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/constants-secrets.md
new file mode 100644
index 0000000000..4004362fcb
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/constants-secrets.md
@@ -0,0 +1,58 @@
+---
+id: constants-secrets
+title: Referencing Constants and Secrets
+---
+
+When building applications in ToolJet, you often need to reuse fixed values (such as URLs or environment flags) or securely handle sensitive information (such as API keys or database credentials). [Workspace Constants and Secrets](/docs/security/constants/) make this process easy, safe, and maintainable, especially when working across multiple apps or with larger teams.
+
+In this guide, you'll learn how to use Workspace Constants and Secrets within your ToolJet apps.
+
+You can create a global constant or secret directly from the ToolJet Dashboard. Once created, these constants and secrets can be referenced by builders within the app-builder.
+
+
+
+## Characteristics and Usage
+
+Both constants and secrets allow you to store reusable values for your apps. However, they serve different purposes and have distinct characteristics as shown below:
+
+| Characteristic | Global Constants | Secrets |
+|-------------------------|:-----------------------------:|:-------------------------:|
+| Components | ✅ | ❌ |
+| Data Queries | ✅ | ✅ |
+| Encrypted in DB | ✅ | ✅ |
+| Masked in Frontend | ❌ | ✅ |
+| Resolved on Client Side | ✅ | ❌ |
+| Resolved on Server Side | ❌ | ✅ |
+| Naming Convention | `{{constants.constant_name}}` | `{{secrets.secret_name}}` |
+
+## Access Control
+
+To maintain security and governance:
+- Only Admins can create, edit, or delete Constants and Secrets.
+- Builders can reference them in their applications but cannot modify them.
+
+## Use Cases
+
+### Reusable Values Across Apps with Global Constants
+
+Imagine you’re building an app that fetches product prices from an API. The base URL of your API is the same across multiple queries.
+
+Instead of hard-coding this URL everywhere, define a Global Constant. Now, if the base URL ever changes, you only need to update it in one place, reducing errors and improving maintainability.
+
+- Name: `API_BASE_URL`
+- Value: `https://api.example.com/v1`
+
+You can now reference it in your queries or custom code:
+
+
+
+### Handling Sensitive Credentials with Secrets
+
+Let’s say your application uses a third-party service such as OpenAI that requires an API key. Storing this key directly in queries or code isn’t a good practice. Instead, define a Secret:
+
+- Name: `OPENAI_API_KEY`
+- Value: `sk_****************`
+
+
+
+Secrets are encrypted and can only be accessed within queries and data sources. They are not accessible in components, ensuring your credentials remain secure.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/controlling-components.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/controlling-components.md
new file mode 100644
index 0000000000..025c39e138
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/controlling-components.md
@@ -0,0 +1,60 @@
+---
+id: control-components
+title: Controlling Components Using Code
+---
+
+Apart from [**events**](/docs/docs/app-builder/events/use-case/csa), Component-Specific Actions (CSAs) can also be triggered using code to modify component properties and state.
+
+Let’s say you want to:
+- Reset a **Form** automatically after submission
+- Show or hide components based on a condition
+- Update a **Text Input** based on another field’s value
+- Disable a button during API calls
+- Change the active tab programmatically
+
+In each of these cases, you can use CSAs with JavaScript or Python queries.
+
+## How It Works
+
+Each component in ToolJet comes with a set of CSAs. Below are some examples of CSAs:
+- setValue() – Sets or updates a component’s value
+- clear() – Clears the value of an input
+- setLoading() – Sets or unsets the loading state
+- setDisable() – Enables or disables a component
+- setVisibility() – Dynamically controls component visibility
+
+You can trigger these actions from your JavaScript or Python queries.
+
+For example, if you have a **Button** that triggers a query and want to show a loader until the data is loaded. You could use `setLoading()` to show a spinner:
+
+```js
+await components.button1.setLoading(queries.getData.isLoading)
+```
+
+## Use Cases
+
+### Pre-fill a Form Field Based on User Selection
+
+When a user selects a product from a **Dropdown**, automatically set the price in a **Text Input** component:
+
+```js
+await components.textInput1.setValue(components.dropdown1.value)
+```
+
+### Clear Fields After Submitting a Form:
+
+After a user submits a **Form**, reset all inputs:
+
+```js
+await components.formInput.resetForm()
+```
+
+### Close the Modal after Form Submission:
+
+If you are using a **Modal** for collecting data, close it once the **Form** has been submitted successfully:
+
+```js
+await components.modal1.close()
+```
+
+Using CSAs in your code lets you dynamically control component behavior based on custom logic.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/fx-dynamic-behaviour.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/fx-dynamic-behaviour.md
new file mode 100644
index 0000000000..99fe660a2e
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/fx-dynamic-behaviour.md
@@ -0,0 +1,57 @@
+---
+title: Using fx for Dynamic Behaviour
+id: fx-dynamic-behaviour
+---
+
+In ToolJet, you can make your applications more interactive by writing logic directly in component properties using the **fx** editor. For instance, you might want to disable a **Button** until all required form fields are filled, or change the color of an input field based on whether the entered value is valid. You can define this conditional logic using JavaScript expressions in the **fx** editor.
+
+This makes it easy to build intuitive interfaces, with components that respond in real time to user actions and data updates.
+
+## How fx Works
+Whenever you see the **fx** icon next to a property in the component settings, it means you can switch to expression mode. Clicking the icon opens an input box where you can write custom logic using JavaScript inside `{{ }}`. You can reference query results, component states, and app-level variables directly within these expressions. ToolJet supports full JavaScript syntax here, including conditional logic, string interpolation, array methods like map, filter, and reduce, and more.
+
+Let’s say you’re building a form that takes user input. You want the **Submit button** to be enabled only if all form validations pass.
+
+With ToolJet’s **fx** support, you can achieve this in the Disabled property of the button component like so:
+
+
+
+This expression disables the **Button** when the **Form** is invalid. No manual toggling needed. Similarly, you could use the same approach to update other properties such as visibility, background color, font size etc. for different components.
+
+If you are new to ToolJet and want to learn how to access component properties, check out [this guide](/docs/app-builder/building-ui/component-state#available-component-states).
+
+## Use Cases
+
+### Loading States
+
+Display loading indicators until the data is loaded.
+
+Example: In an app where you are loading data in the table component, you might want to show a loading spinner while fetching employee data.
+
+
+
+### Conditional Styling
+
+Apply conditional styling (colors, fonts, sizes) based on values from queries or application state.
+
+**Example:** In an employee directory with a user list, you can display different background colors on **Table** cells based on whether the user is active or inactive.
+
+
+
+### Form Validation
+
+Enable or disable submit buttons based on the validity of form inputs.
+
+**Example:** In **Forms**, you can enable the Submit button only when all required fields are correctly filled.
+
+
+
+### Conditional Visibility
+
+Show or hide components based on specific conditions.
+
+**Example:** In an employee directory application, within the personal details **Form**, you can conditionally display a **Text Input** for entering a custom country name when the user selects “other” from the country dropdown.
+
+
+
+Using the **fx** editor, you can easily add dynamic behavior to your applications with minimal code.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/manage-variables.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/manage-variables.md
new file mode 100644
index 0000000000..9bdd2c22c7
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/manage-variables.md
@@ -0,0 +1,128 @@
+---
+id: managing-variables
+title: Managing Variables
+---
+
+Variables in ToolJet allow you to store and manage temporary data across pages or within a single page. They’re useful for passing values between components, persisting state, and building dynamic applications.
+
+This guide explains how to manage [variables](/docs/beta/app-builder/events/use-case/variables) using code in your applications.
+
+## Set, Get, and Unset Variables
+
+Setting, getting, and unsetting variables lets you control the state of a variable. Use set to create variables or update their values, get to access them in components or queries, and unset to delete them.
+
+### Set Variables
+
+To set a variable in an application using code in a RunJS or RunPy query, use the `setVariable` function and pass the variable name and value.
+
+```js
+actions.setVariable("", "")
+```
+
+**Example:** If you’re building an internal tool for order management and want to store the *orderId* of a newly created order. You can use the following code:
+
+```js
+actions.setVariable('currentOrderId', 'ORD-10293')
+```
+
+Similarly, if you want to set a page variable use the `setPageVariable` function:
+
+```js
+actions.setPageVariable("", "")
+```
+
+**Example:** If you want to set a page variable named *userPreferences*, with an object containing all the user preferences, like `{theme:'dark', language:'en'}`, you can use the following code:
+
+```js
+actions.setPageVariable('userPreferences', { theme: 'dark', language: 'en' });
+```
+
+### Get Variables
+To access variables you can use the `getVariable` and `getPageVariable` functions. These functions take one argument: the name of the variable.
+
+```js
+// To get app-level variable
+actions.getVariable("");
+
+// To get page-level variable
+actions.getPageVariable("");
+```
+
+**Example:** If you previously stored a variable named *currentOrderId* and now want to access it, You can use the code below:
+
+```js
+const orderId = actions.getVariable('currentOrderId');
+```
+
+### Unset Variables
+To unset(delete) a variable, you can use the `unsetVariable` and `unsetPageVariable` functions. These functions take one argument: the name of the variable.
+
+```js
+// To delete app-level variable
+actions.unsetVariable("")
+
+// To delete page-level variable
+actions.unsetPageVariable("")
+```
+
+**Example:** If you want to unset a page variable named *userPreference*, you would write:
+
+```js
+actions.unsetPageVariable('userPreferences');
+```
+
+## Use Cases
+
+### Sharing Data Across Pages
+
+You can share data across different pages by setting a variable on one page and accessing it on another.
+
+For instance, in a content management system, the homepage might display a list of posts (as shown in the image below). When a user clicks the **View Post** button, they’re taken to a new page to see the full content. To enable this, the *postId* is stored as a global variable so it can be accessed on both the homepage and the post details page.
+
+
+
+On the homepage, you could add a click event handler to the **View Post** button that sets a variable called *selectedListViewIndex* with the ID of the selected post. Then, on the second page, you could retrieve this variable and use it to fetch the full post from the database.
+
+
+
+```js
+// Saving the post ID to a variable
+actions.setVariable("selectedListViewIndex", components.postList.selectedRow.id);
+
+// Retrieving the post ID
+const postId = actions.getVariable("selectedListViewIndex");
+```
+
+### Setting Up Form Payload for a Multi-Step Form
+
+If you’re building a multi-step **Form**, each step may require different fields and appear on separate pages. You can use variables to construct the payload based on the currently active page.
+
+Let’s say your **Form** has three steps: personal details, educational background, and work experience. Each step has its own set of fields. If you want to construct a final payload to be sent as the body when the submit button is clicked on the last step, you can create a RunJS query that checks which step is active and constructs the payload accordingly. Here’s how you might implement this:
+
+```js
+let payload = {};
+
+if (page.handle === "personalDetails") {
+ payload.firstName = components.firstName.value;
+ payload.lastName = components.lastName.value;
+ payload.email = components.email.value;
+}
+else if (page.handle === "education") {
+ payload.educationLevel = components.educationLevel.value;
+ payload.major = components.major.value;
+ payload.graduationYear = components.graduationYear.value;
+}
+else if (page.handle === "workExperience") {
+ payload.companyName = components.companyName.value;
+ payload.startDate = components.startDate.value;
+ payload.endDate = components.endDate.value;
+ payload.jobTitle = components.jobTitle.value;
+}
+
+actions.setVariable("formPayload", payload);
+```
+
+You can now pass this payload to a query that sends it to your backend API endpoint.
+
+Variables help maintain data across pages, while page variables help manage localized, page-specific logic. Use page variables for temporary, page-specific UI state, and use app-level variables when data must persist across multiple pages.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/transform-data.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/transform-data.md
new file mode 100644
index 0000000000..d66b7d1676
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-code/transform-data.md
@@ -0,0 +1,145 @@
+---
+id: transform-data
+title: Transforming Data
+---
+
+Data isn’t always available in a ready-to-use format from a single source. Often, you’ll need to transform or combine data before displaying it. Common use cases include:
+
+- Merging results from multiple data sources
+- Restructuring API responses before referencing them to components
+- Applying business logic such as filtering, sorting, or grouping
+- Formatting fields like dates, currency values, or nested JSON objects
+
+If you need to transform data from a single query, you can use the [transformation](/docs/beta/app-builder/connecting-with-data-sources/transforming-data) option within the query.
+
+
+
+However, if your use case involves combining data from multiple queries or components, you’ll need to use RunJS or RunPy queries.
+
+For example, let's say you're building an inventory management application and want to display a list of items along with their current stock levels. You have inventory data stored in a PostgreSQL database and product data coming from a ToolJet Database. To display the item names alongside their current stock levels, you would need to merge the results from these two queries using a RunJS query as shown below.
+
+
+
+ToolJet allows you to write RunJS or RunPy queries to perform these transformations without requiring changes to your backend. This guide demonstrates how to transform data for real-world use cases.
+
+## How It Works
+
+ToolJet allows you to access data from:
+- Configured data source queries (e.g., PostgreSQL, REST APIs, MongoDB, etc.)
+- Component values (like inputs, dropdowns, tables)
+
+With RunJS or RunPy queries, you can write code to manipulate data from multiple sources.
+
+## Use Cases
+
+### 1. Merging Data from Two APIs
+
+Let's say you want to show a list of users with their order counts. The user data comes from a REST API, and the order data comes from a MySQL database. Now, if you want to show a combined list of users along with their respective order counts, you can use a RunJS or RunPy query to combine the results:
+
+```js
+// Assuming getUsers and getOrders are already defined as queries
+// Run queries to fetch users and their orders
+await queries.getUsers.run();
+await queries.getOrders.run();
+
+// Retrieve data from both queries
+const userList = queries.getUsers.getData(); // Array of user records
+const orderList = queries.getOrders.getData(); // Array of order records
+
+// Enrich each user with their corresponding order count
+const usersWithOrderCount = userList.map(user => {
+ // Find all orders placed by the current user
+ const userOrderHistory = orderList.filter(order => order.userId === user.id);
+
+ return {
+ ...user,
+ orderCount: userOrderHistory.length
+ };
+});
+
+return usersWithOrderCount;
+```
+
+
+Data from getUsers query
+
+```js
+[
+ { id: 1, name: "Alice", email: "alice@example.com" },
+ { id: 2, name: "Bob", email: "bob@example.com" },
+ { id: 3, name: "Charlie", email: "charlie@example.com" },
+ { id: 4, name: "David", email: "david@example.com" },
+ { id: 5, name: "Eva", email: "eva@example.com" },
+ { id: 6, name: "Frank", email: "frank@example.com" }
+]
+```
+
+
+
+
+
+Data from getOrders query
+
+```js
+[
+ { id: 101, userId: 1, total: 120.00 },
+ { id: 102, userId: 1, total: 45.50 },
+ { id: 103, userId: 2, total: 89.99 },
+ { id: 104, userId: 1, total: 60.00 },
+ { id: 105, userId: 3, total: 150.00 },
+ { id: 106, userId: 3, total: 200.00 },
+ { id: 107, userId: 4, total: 75.00 },
+ { id: 108, userId: 5, total: 50.00 },
+ { id: 109, userId: 4, total: 90.00 }
+]
+```
+
+
+
+
+
+Data from usersWithOrderCount query
+
+```js
+[
+ { id: 1, name: "Alice", email: "alice@example.com", orderCount: 3 },
+ { id: 2, name: "Bob", email: "bob@example.com", orderCount: 1 },
+ { id: 3, name: "Charlie", email: "charlie@example.com", orderCount: 2 },
+ { id: 4, name: "David", email: "david@example.com", orderCount: 2 },
+ { id: 5, name: "Eva", email: "eva@example.com", orderCount: 1 },
+ { id: 6, name: "Frank", email: "frank@example.com", orderCount: 0 }
+]
+```
+
+
+
+Now you can reference this data in your app, for instance, in a **Table** component.
+
+### 2. Grouping and Sorting Data with Custom Business Logic
+
+Let's say you have a list of products and want to group them by category and sort each group by stock (highest to lowest). This helps display organized inventory in a component like a nested list or grouped table. You can use a RunJS query to transform the data:
+
+```js
+// Trigger the query and retrieve the data
+await queries.getProducts.run();
+const products = queries.getProducts.getData();
+
+const grouped = {};
+
+products.forEach(product => {
+ const category = product.category;
+ if (!grouped[category]) {
+ grouped[category] = [];
+ }
+ grouped[category].push(product);
+});
+
+// Sort each category group by stock in descending order
+for (const category in grouped) {
+ grouped[category].sort((a, b) => b.stock - a.stock);
+}
+
+return grouped;
+```
+
+This is how you can use RunJS or RunPy queries to transform data in ToolJet. Keep in mind that while writing code offers flexibility, it can also introduce complexity. Always consider performance implications when writing complex transformations.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-theme.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-theme.md
new file mode 100644
index 0000000000..d67eb71575
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/custom-theme.md
@@ -0,0 +1,121 @@
+---
+id: custom-theme
+title: Custom Theme
+---
+
+ToolJet supports Custom Themes at the workspace level, giving teams complete control over the look and feel of their applications. With this feature, you can define and manage multiple themes and apply them across your apps in a consistent and reusable way.
+
+Each workspace can have one or more themes configured, and any application within that workspace can use any of the defined themes. This helps in maintaining visual consistency across applications, improves brand alignment, and enhances user experience.
+
+## What is a Custom Theme?
+A Custom Theme allows you to customize the UI components of your ToolJet apps by configuring a set of visual styles such as:
+
+- Brand Colors (Primary, Secondary, Tertiary)
+
+- Text colors
+
+- Border and surface styling
+
+- System state colors (e.g., error, success)
+
+
+
+You can configure these settings for both light and dark modes, and instantly preview changes using the built-in preview panel.
+
+Each theme includes configurable values like:
+
+- Primary: Used for buttons, links, focus states, and other interactive elements.
+- Secondary (optional): For additional visual hierarchy.
+- Tertiary (optional): Useful for complex color relationships.
+
+Once saved, the theme becomes available for use in any app under the workspace.
+
+
+
+## Why Use Custom Themes?
+Custom Themes empower your organization by:
+
+- Brand consistency: Align your internal tools with your company’s visual identity.
+
+- Reusability: Define once, use across multiple apps.
+
+- Customization: Update the look of all your apps in one go by editing the theme.
+
+- Collaboration: Teams working on different apps can maintain a unified design system.
+
+This is especially useful for teams with apps across different environments (e.g., internal tools, client-facing apps, admin panels) where each might need a slightly different yet consistent visual identity.
+
+## How to Use Custom Themes
+
+Using Custom Themes in ToolJet involves two simple steps — **creating the theme** and **applying it to your applications**.
+
+### 1. Create a Custom Theme
+
+- Go to your **Workspace Settings**.
+- Click on the **Custom Theme** tab.
+- Click **Create new theme**.
+- Configure your theme styles:
+ - Set your **Brand colors**: Primary, Secondary, Tertiary
+ - Define **Text**, **Border**, **System status**, and **Surface** colors
+ - Choose styles for both **Light** and **Dark** modes
+- Click **Save** once you're done.
+
+ You can create multiple themes as per your needs — for different teams, environments, or clients.
+
+### 2. Apply the Theme to Your Application
+
+- Open the app where you want to use the theme.
+- Click the **Settings icon** in the **left sidebar** to open **Global Settings**.
+- Scroll down to the **Theme** section.
+- You’ll see a dropdown showing the currently selected theme (usually the default).
+- Click the dropdown to view and select from all your configured themes.
+
+
+
+Once selected, your app will now use the chosen theme as the base style for components.
+
+### 3. Use Theme Styles in Your Components
+
+To make your components adopt the theme styles:
+
+- Select a component in the app canvas.
+- Go to the **Style** tab of the component.
+- Wherever color can be set (background, border, text), you’ll see a **Theme** option next to the color picker. Once selected you'll see the list of theme colors such as Brand/Primary, Brand/Secondary, Text/Primary, etc.
+
+These options map directly to what you configured during theme setup.
+
+
+
+
+
+Once components are styled using theme options, changing the theme from Global Settings will instantly update all those components, making your app visually consistent and easy to update.
+
+## Scenarios
+Here are some scenarios where custom themes shine:
+
+- Brand-specific apps: Create different themes for different brands/clients your company serves.
+
+- Dark & light mode toggle: Provide a seamless visual switch for end-users between light and dark modes.
+
+- Multi-team organizations: Let each team within your org create and maintain their own theme without affecting others.
+
+## Example
+
+Here’s an example showing how an application interface looks before and after applying a custom theme.
+
+### Before (Default Theme)
+
+This is the default ToolJet UI without any custom theme applied. It uses the standard branding and neutral colors.
+
+
+### After (Custom Theme Applied)
+
+
+
+This is the same application after applying the "Coral" custom theme. Notice the button color, primary accents, and overall visual alignment now reflect the chosen palette.
+
+
+
+By simply configuring a theme once at the workspace level, you can instantly apply a fresh look across all apps, improving usability, clarity, and brand identity.
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/inspector.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/inspector.md
new file mode 100644
index 0000000000..9d5640f86f
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/inspector.md
@@ -0,0 +1,76 @@
+---
+id: inspector
+title: Inspecting Values
+---
+
+ToolJet's Inspector is a built-in utility that provides real-time visibility into the data and state of your application. With Inspector, developers can quickly diagnose issues, understand the flow of data, and ensure that all components, queries, and variables are functioning as intended.
+
+Inspector is accessible via the left sidebar in the App Builder. It is divided into six main sections, each offering a different perspective into your application’s runtime environment:
+
+- [Queries](#queries)
+- [Components](#components)
+- [Globals](#globals)
+- [Variables](#variables)
+- [Page](#page)
+- [Constants](#constants)
+
+## Accessing Inspector
+
+Values that are displayed inside the inspector can be referenced in other components and queries to create interactive applications. You can access the current state of components, queries, variables, page handle, and more directly from the inspector.
+
+You can refer to a value using the dot notation (e.g., `{{components.numberInput1.value}}`), or hover over any property in the inspector to copy its reference path. This makes it easy to connect components, reuse data, and configure logic throughout your application without writing extra code.
+
+For example, let's say you have a **Table** displaying a list of users, and you want to fetch the details of a particular user when they are selected. You can refer to the selected row's data using the reference path in the Inspector.
+
+You can either type this path manually or hover over the property in the Inspector to copy its path directly. This path can then be used in your query to refer to the value. Additionally, you can add an event handler to the table to automatically run this query whenever a user is selected.
+
+
+
+### Queries
+
+Under the Queries section, you can inspect the specifics of any query you’ve created. The data for a query is only visible after the query has been executed. This allows you to verify the output and troubleshoot any issues with data retrieval or manipulation. The Inspector exposes the following properties for each query:
+
+- **isLoading** – A boolean indicating whether the query is currently in progress. This can be used to control the loading state of components that depend on the query's result.
+- **data** – The transformed data returned by the query.
+- **rawData** – The original response fetched from the data source.
+- **id** – A unique identifier automatically assigned to every query in ToolJet.
+
+Refer to the [Binding Data with Component](/docs/beta/app-builder/connecting-with-data-sources/binding-data-to-components) guide to learn how to bind the query data to the component.
+
+### Components
+
+The Components section provides a detailed view of each component present on your app’s canvas. You can see the current state, properties, and values of each component, helping you understand how data flows through your application and how components interact with each other. Only the components of the current page are visible in this section.
+
+Each component exposes a different set of states and CSAs based on its functionality. For example:
+- A **Text** component exposes a `text` state and a `setText` CSA.
+- A **Checkbox** component exposes a `label` state and a `setValue` CSA.
+
+To learn more about a specific component and its exposed properties, refer to the [individual component](/docs/beta/app-builder/building-ui/component-library) guide.
+
+Refer to the [Accessing Component State](/docs/beta/app-builder/building-ui/component-state) guide to learn how to use component state.
+
+### Globals
+
+By using the Globals properties in the Inspector, you can view various details about your application and its environment, such as:
+- **Current User** - Information about the logged-in user, including email, name, avatar, groups, roles, and SSO details. Useful for building role-based UI or showing personalized content.
+- **Environment** - Indicates the current ToolJet environment — development, staging, or production.
+- **Mode** - Indicates whether the app is opened in the editor or not.
+- **Theme** - Refers to the active UI theme (light or dark). You can use this to dynamically style components based on the selected theme.
+- **URL Params** - These are query parameters appended to the page URL, commonly used to pass data between pages.
+
+### Variables
+
+The Variables section in the Inspector lets you view all app-level variables available within the current application. These variables can be used to store and share data across components and queries. You can inspect their current values here, making it easier to debug and manage dynamic behavior in your app.
+
+### Page
+
+The Page section displays page-specific properties (such as page handle and name) and page-level variables. Unlike app-level variables, page-level variables are only accessible within their respective pages.
+
+- **handle** - The page handle is a unique identifier used to generate a shareable URL for the page. It is appended as a slug to the end of your application URL.
+- **id** - A unique identifier automatically assigned to every page in ToolJet.
+- **name** - The display name of the page, shown in the app's navigation menu. Set by the user.
+- **variables** - List of page level variables in the key-value pair.
+
+### Constants
+
+Workspace Constants are predefined values that you can use across different applications within your workspace. They are useful for storing frequently used data such as API URLs, configuration settings, or sensitive information like API keys and database credentials. In the inspector, you can view all constants as key-value pairs, secret constant values are masked for security.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/logs.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/logs.md
new file mode 100644
index 0000000000..b85bc83923
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/debugging/logs.md
@@ -0,0 +1,65 @@
+---
+id: understanding-logs
+title: Understanding Logs
+---
+
+Debugging is a critical part of building applications, and ToolJet makes it easier with a built-in Debugger that tracks and displays logs in real time. Whether it’s a query success or failure, or a component malfunction, the debugger logs will help you get to the root of the problem quickly.
+
+This guide walks you through making the most of ToolJet’s logs, with practical examples that explain why they matter and how to use them effectively.
+
+## How Logs Work
+
+ToolJet captures real-time events and organizes them in the debugger panel.
+
+When you’re in the app-builder, you can look for the Debugger icon on the left sidebar. Click it to open the debugger panel. This panel becomes your main console for inspecting the query execution results (success/failure)
+and component-level issues.
+
+
+
+## Custom Logs
+
+In ToolJet, you can use custom log methods to capture errors, debug info, and runtime events in your app. These functions work similarly to JavaScript’s console.log() but offer clearer intent and structured logging.
+
+### Log Errors
+
+Logs an error. Useful for failed API calls, exceptions, or critical issues.
+
+```js
+actions.logError("API failed");
+```
+
+### Log Information
+
+Logs informational messages. Use for successful actions or state changes.
+
+```js
+actions.logInfo("User logged in");
+```
+
+### Log Messages
+
+Generic log for debugging or checkpoints.
+
+```js
+actions.log("Reached step 2");
+```
+
+## Use Case
+
+### Debugging Queries
+
+Let’s say you’re building an app and have integrated a REST API to fetch products. You’ve connected this query to a **Table** component, but when you run it, the data doesn’t show up. To troubleshoot this, open the debugger and navigate to the Logs tab. There, you’ll find detailed information about the query execution, including:
+- Whether the query succeeded or failed
+- Any error messages returned
+- The request payload and response body
+- The status code returned by the server
+
+This information helps you identify what went wrong and where to start troubleshooting.
+
+### Troubleshooting Component Related Issues
+
+Let’s say you’ve fetched user data from a database using a query, and connected it to a table component. The query runs without errors, but the table still appears empty. To investigate this, open the debugger and head to the Error Logs tab. Here, you’ll find error logs related to the component’s behavior, including:
+- Errors related to misconfigured properties
+- Invalid expressions used in bindings
+
+These logs help you determine whether the issue stems from component configuration or its interaction with the query result, making it easier to fix problems and get your UI working as expected.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/event-triggers.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/event-triggers.md
new file mode 100644
index 0000000000..120e2daed8
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/event-triggers.md
@@ -0,0 +1,40 @@
+---
+id: event-triggers
+title: Setting Up Events
+---
+
+Events define how your app should respond when a user interacts with a component or when a specific system condition is met. Whether it's clicking a **Button** component, selecting an item from a **Dropdown**, or completing a query, events let you tie in logic that makes your app interactive and reactive.
+
+You can use event triggers to run queries, update variables, show alerts, navigate to different pages, and more. Each event can be configured to trigger one or more actions in sequence, allowing you to build complex logic flows easily. Refer to the individual [component guide](/docs/beta/app-builder/building-ui/component-library) to see the full list of supported events, and check out the [Action Reference](/docs/category/actions-reference) for all available actions.
+
+## Configuring an Event Handler
+
+Suppose you're building a feedback form using a **Form** component that submits user input to a database whenever the user clicks on the submit button. To achieve this, you can configure the submit button to trigger a query when clicked.
+
+First, create a query and name it *addData*. This query inserts **Form** values into the database. Then, configure the **Button** with the following event handler:
+- Event: **On Click**
+- Action: **Run Query**
+- Query: **addData**
+
+
+
+This setup ensures that every time the button is clicked, the **Form** data is sent to your database.
+
+
+
+## Configuring Sequential Event Handler
+
+Continuing the previous example. After submitting the form, you may want to update the UI by fetching the latest data. To do this, create a new query and name it *fetchData* that retrieves updated records from the database.
+
+Next, configure an event handler that runs sequentially after the *addData* query succeeds:
+- Event: **Query Success**
+- Action: **Run Query**
+- Query: **fetchData**
+
+
+
+This setup ensures that the *fetchData* query is triggered automatically when the *addData* query completes successfully.
+
+
+
+Whether it's submitting a form, running a query, or updating your UI, events and actions let you define dynamic, logic-driven behavior without writing backend code.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/overview.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/overview.md
new file mode 100644
index 0000000000..fc4dafde8e
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/overview.md
@@ -0,0 +1,41 @@
+---
+id: overview
+title: Overview
+---
+
+In ToolJet, you can build dynamic, logic-driven applications using Events, Actions, Variables, and Component-Specific Actions (CSAs). These features let you define how your app responds to user interactions and system events without writing any backend code. Each component has a unique set of available events and CSAs based on its functionality. Refer to the [individual component](/docs/beta/app-builder/building-ui/component-library) guides for more details.
+
+## Events
+
+Events are triggers that respond when certain conditions are met — either through user interaction (for example, clicking a **Button** component or selecting a **Dropdown** option) or system-level changes (for example, the completion of a query). You can configure events using event handlers on components or queries. Each handler defines the trigger and the actions that should follow.
+
+For example, when a user clicks a **Button**, it can trigger a query to refresh data. Once that query completes, a second event can run to show a confirmation alert.
+
+
+
+## Actions
+
+Actions specify the outcome when an event is triggered. ToolJet supports a wide range of actions such as running queries, showing alerts, navigating between pages, copying text to the clipboard, and more. You can configure actions directly within event handlers or [dynamically using JavaScript](/docs/beta/app-builder/custom-code/control-components) via RunJS queries. For a complete list of available actions, refer to the [Action Reference](/docs/category/actions-reference) guide.
+
+
+
+## Component Specific Actions (CSAs)
+
+Component-Specific Actions (CSAs) are built-in functions that allow you to control a component's state and behavior at runtime. Each component has its own set of CSAs based on its capabilities. For example, a **Text** component supports the `setText()` action, while a **Radio Button** component offers `selectOption()`.
+
+
+
+## Variables
+
+Variables let you store and manage data either across your entire application or within specific pages. They're essential for maintaining state, controlling logic, and creating personalized user experiences.
+ToolJet supports the following types of variables:
+
+- App-level variables – accessible throughout the entire app.
+- Page-level variables – scoped to a specific page.
+
+
+
+
+
+In addition, ToolJet provides built-in [exposed variables](/docs/beta/app-builder/building-ui/component-state) for components and queries, which represent their current runtime state.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/csa.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/csa.md
new file mode 100644
index 0000000000..0807e915ad
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/csa.md
@@ -0,0 +1,40 @@
+---
+id: csa
+title: Controlling Component State
+---
+
+Component-Specific Actions (CSAs) are built-in functions that allow you to control the component state and behavior in the application. Each component has its own set of CSAs based on its capabilities.
+
+For example, a Text component supports the `setText()` action, while a Radio Button component offers `selectOption()`.
+
+You can trigger these actions via event handlers or by using expressions in your queries. Refer to the individual [component guide](/docs/beta/app-builder/building-ui/component-library) for a complete list of supported CSAs.
+
+## Controlling Components
+
+Suppose you've used a **Form** component to build a feedback form and want to clear all inputs after the data is submitted to the database. ToolJet provides a `resetForm` function to help with this, which can be triggered in two ways:
+- [Using an Event Handler](#using-an-event-handler)
+- [Using a JS Expression in a Query](#using-a-javascript-expression-in-a-query)
+
+### Using an Event Handler
+
+Suppose you have a query named **addData**, which is being used to insert the form data into the database. To clear the **Form** using an event handler, add the following configuration to your **addData** query:
+- Event: **Query Success**
+- Action: **Control Component**
+- Component: **feedbackForm** (Select your **Form** component from the dropdown)
+- Actions: **Reset Form**
+
+
+
+This setup ensures that the **Form** component is cleared automatically when the **addData** query completes successfully.
+
+
+
+### Using a JavaScript Expression in a Query
+
+Alternatively, you can reset the **Form** directly within your query by appending this JavaScript expression:
+
+```js
+await components.feedbackForm.resetForm()
+```
+
+Component-Specific Actions give you precise control over how components behave at runtime, making your applications more interactive and responsive.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/page-nav.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/page-nav.md
new file mode 100644
index 0000000000..29b6bc9ac1
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/page-nav.md
@@ -0,0 +1,43 @@
+---
+id: page-nav
+title: Implementing Navigation Using Actions
+---
+
+ToolJet offers page navigation out of the box. For any custom navigation needs, such as implementing a navigation bar, you can use event handlers and actions. You can also pass query parameters during navigation, making it easy to share context between pages.
+
+## Building Custom Navigation Menu
+
+Follow these steps to build a custom navigation menu:
+
+1. Add a container to serve as your navigation wrapper.
+2. Place icons, text or button components inside the container for each page you want to link to.
+3. For each navigation item:
+ - Select the component (icon or text).
+ - Add an event handler.
+ - Event: **On click**
+ - Action: **Switch page**
+ - Page: *Select the target page from the dropdown*
+
+
+
+Once configured, clicking on a navigation item will take the user to the corresponding page.
+
+
+
+## Passing Data Between Pages
+
+Suppose you're building a ticket management system where Page 1 displays a list of all tickets, and clicking on a ticket redirects the user to Page 2, which shows the details of the selected ticket. Here's how to set it up:
+
+1. On Page 1, display all the tickets using a **Table** component.
+2. Add an event handler to the table:
+ - Event: **Row clicked**
+ - Action: **Switch page**
+ - Page: **Page 2** *(Select the ticket details page.)*
+ - Query parameters:
+ - **Key**: `ticketId`
+ - **Value**: `{{components.ticketTable.selectedRow.ticket_id}}`
+3. On Page 2, design the UI to display the ticket details.
+4. Use the query parameter to fetch or display the selected ticket's data:
+ - Reference it with: `{{globals.urlparams.ticketId}}`
+
+This setup enables users to click on a ticket in the table and seamlessly navigate to a detailed view of that specific ticket, with the necessary data passed between pages using query parameters.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/variables.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/variables.md
new file mode 100644
index 0000000000..cd006e241a
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/events/use-case/variables.md
@@ -0,0 +1,69 @@
+---
+id: variables
+title: Setting Variables
+---
+
+Variables let you store and manage data across your entire application. You can use them to manage component state, control logic, store user input or to create a personalized user experiences. By setting a value once in a variable, you can reuse it across different parts of your app. This makes your app easier to build and maintain, without needing to store everything in a database.
+
+ToolJet supports two types of variables:
+
+- **App-level variables** are available across all pages in your app. To set an app-level variable, use the `setVariable` action.
+- **Page-level variables** are only available on the page where they are created. To set a page-level variable, use the `setPageVariable` action.
+
+## Set Variables
+
+### App Level Variable
+
+Suppose you are building a multi-page application where, on the first page, you ask the user for their name and want to use it in the other pages. Here’s how to do it:
+1. Add a **Text Input** component to collect the user’s name.
+2. Add a **Button** component to submit the name.
+3. Set up this event handler on the **Button** component:
+ - Event: **On Click**
+ - Action: **Set variable**
+ - Key: `username` *(Variable name of your choice.)*
+ - Value: `{{components.usernameinput.value}}` *(Refer to the user input in the **Text Input** component.)*
+
+
+
+When the user clicks the **Button** component, their name will be stored in the app-level variable `username`. You can access this variable anywhere in your app with this syntax:
+
+```js
+{{variables.username}}
+```
+
+### Page Level Variable
+
+Now, suppose you have a **Form** in your application and want to store the user’s contact number only on that page when they submit the **Form**. To do this, set this event handler on the **Button** component:
+
+- Event: **On Click**
+- Action: **Set page variable**
+- Key: `contact` *(Variable name of your choice.)*
+- Value: `{{components.feedbackForm.data.contact.value}}` *(Refer to the user input in the **Number Input** component.)*
+
+When the user clicks on the **Button** component, their contact number will be saved in a page-level variable named `contact`. This variable can only be used on that specific page with this syntax:
+
+```js
+{{page.variables.contact}}
+```
+
+## Unset Variables
+
+### App Level Variables
+
+In your multi-page app, you may want to clear (unset) the `username` variable when the user clicks the **Button** component named "Finish" on the last page. To do this, set the following event handler on the **Button** component:
+
+- Event: **On Click**
+- Action: **Unset variable**
+- Key: `username` *(Variable name you want to unset.)*
+
+### Page Level Variables
+
+In your **Form** app, you might want to clear the page-level `contact` variable when the user clicks the **Button** component named "Next Page". To do this, set this event handler on the **Button** component:
+
+- Event: **On Click**
+- Action: **Unset page variable**
+- Key: `contact` *(Variable name you want to unset.)*
+
+:::info
+You can also manage variables using code. Refer to the [Manage Variables](/docs/app-builder/custom-code/managing-variables) guide for more information.
+:::
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-export-apps.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-export-apps.md
new file mode 100644
index 0000000000..cd735ac09a
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-export-apps.md
@@ -0,0 +1,61 @@
+---
+id: importing-exporting-applications
+title: Import and Export Applications
+---
+
+This documentation explains the process of exporting and importing applications in ToolJet.
+
+
+
+## Exporting Applications
+
+- Navigate to the dashboard.
+- Click on the settings icon located in the top right corner of the application.
+- Click on the **Export app** button.
+
+
+
+
+
+- If you select `Export All`, all the versions of the application will be exported in JSON format. If you select `Export selected version`, only the selected version will be exported in JSON format.
+- Ticking the `Export ToolJet table schema` checkbox will also export the related ToolJet Database table schemas with your application. In this case, when you import the application in a workspace, the related ToolJet Database tables will also be created.
+
+
+
+
+
+
+
+
+
+
+## Importing Applications
+
+- Navigate to the dashboard.
+- Click on the ellipses on the **Create new app** button and select `Import`.
+
+
+
+
+
+- After clicking on `Import`, choose the relevant JSON file that you previously downloaded during the application export process.
+
+
+
+
+
+
+
+
+## Module Behavior During Application Import and Export
+
+**Import**:
+
+- When you import an application, the platform automatically checks for any existing modules with matching names in your workspace or instance. If a module with the same name already exists, the imported application connects to the existing module, avoiding duplication.
+- However, if no matching module is found, the platform creates a new module from the imported JSON file.
+- This approach ensures that your application imports smoothly while maintaining consistency and preventing redundant modules.
+
+**Export**:
+
+- When you export an application, all associated modules linked to the application are automatically included in the export.
+- This ensures that any reusable components or features built as modules are preserved and can be seamlessly imported along with the app into any other workspace.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-js.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-js.md
new file mode 100644
index 0000000000..c79035c2f7
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-js.md
@@ -0,0 +1,125 @@
+---
+id: runjs
+title: Using RunJS
+---
+
+
+ToolJet allows you to use external JavaScript libraries such as Compromise for natural language processing or PapaParse for parsing CSV data in your application using RunJS queries. This helps you avoid writing complex logic from scratch.
+
+This guide walks you through the process of importing and utilizing these libraries effectively.
+
+## Choosing Libraries
+
+You can import various JavaScript libraries using their Content Delivery Network (CDN) links. Find the CDN links for your desired open-source projects on [jsDelivr](https://www.jsdelivr.com/).
+
+## How to Import Libraries
+
+Let’s walk through how to import libraries using RunJS. For example, we’ll use:
+
+- [Compromise](https://github.com/spencermountain/compromise): for natural language processing
+- [PapaParse](https://www.papaparse.com/): for parsing CSV data
+
+### Create a RunJS Query
+
+Open the query panel and create a new **RunJS** query.
+
+### Add the Following Code Snippet
+
+```js
+// Function to add script dynamically
+function addScript(src) {
+ return new Promise((resolve, reject) => {
+ const scriptTag = document.createElement('script');
+ scriptTag.setAttribute('src', src);
+ scriptTag.addEventListener('load', resolve);
+ scriptTag.addEventListener('error', reject);
+ document.body.appendChild(scriptTag);
+ });
+}
+
+try {
+ await addScript('https://cdn.jsdelivr.net/npm/compromise@13.11.3/builds/compromise.min.js');
+ await addScript('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js');
+ await actions.showAlert("success", "Compromise and PapaParse imported");
+} catch (error) {
+ console.error(error);
+}
+```
+
+After adding the code, click on the **Run** button in the query panel. An alert will appear with the message "Compromise and PapaParse imported".
+
+
+:::tip
+Enable the **Run this query on application load** option in the query settings to make the libraries available throughout the application as soon as the app is loaded.
+:::
+
+## Use Cases
+
+Let’s look at how you can apply these libraries in real-world use cases.
+
+### Extracting Action Items from Meeting Notes using Compromise (NLP)
+
+Let's say you are building an internal project management tool where users paste raw meeting notes. You want to auto-extract action items, dates, and team names. You can use the following code to process the notes using NLP:
+
+```js
+const notes = nlp("Met with John, Priya, and Marcus from the marketing team on Thursday. Discussed launch strategy for the Q3 campaign. Priya will draft the blog post by next Tuesday. John to prepare budget estimates. Marcus will handle email outreach by Friday. Next sync on July 10th.");
+
+const people = notes.people().out('array');
+const actions = notes.sentences().filter(s => s.has('#Verb')).out('array');
+
+return { people, actions };
+```
+
+Preview the output in the query manager or click **Run** in the query panel to see the output as shown below.
+
+
+
+
+### Bulk Upload Employee Data into an Employee Directory
+
+Let’s say your HR team maintains employee records in spreadsheets and wants a way to import this data quickly into your internal Employee Directory application. You can use the following code to clean up the data:
+
+```js
+const csvData = components.filepicker1.file[0].content;
+
+const parsedData = Papa.parse(csvData, {
+ header: true,
+ skipEmptyLines: true
+});
+
+return parsedData;
+```
+
+
+
+## Built-in JavaScript Libraries
+
+ToolJet comes with some essential JavaScript libraries preloaded in the RunJS environment, so you don’t need to import them manually:
+- [Moment.js](https://momentjs.com/docs/) – for date/time formatting and manipulation
+- [Lodash](https://lodash.com/docs/) – for working with arrays, objects, and collections
+- [Axios](https://axios-http.com/docs/intro) – for making HTTP requests
+
+You can use these libraries directly in RunJS to simplify your logic, transform data, or integrate with APIs.
+
+Example:
+
+```js
+// Format Timestamps for UI Display
+const raw = '2025-06-05T15:42:00Z';
+return moment(raw).format('MMM D, YYYY, h:mm A');// "Jun 5, 2025, 9:12 PM"
+
+// Deep Comparison of Records with Lodash
+const a = { name: 'Alice', dept: { id: 1, name: 'HR' } };
+const b = { name: 'Alice', dept: { id: 1, name: 'HR' } };
+
+return _.isEqual(a, b); // true
+
+// Posting JSON Data with Error Handling
+axios.post('https://api.company.com/inventory', {
+ name: 'Laptop',
+ quantity: 10,
+}).then(res => res.data)
+ .catch(err => console.error(err.response.data));
+```
+
+Use RunJS to easily import and leverage external JavaScript libraries in your ToolJet app for advanced data processing and logic.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-py.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-py.md
new file mode 100644
index 0000000000..5fbeb26efd
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/import-libraries/import-external-lib-py.md
@@ -0,0 +1,126 @@
+---
+id: runpy
+title: Using RunPy
+---
+
+In this guide, we will learn to import Python libraries in your applications.
+
+If you are new to using RunPy queries, check out our [guide](/docs/data-sources/run-py/) on how to get started with RunPy. ToolJet supports installing libraries using **micropip**. Check out [this](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) documentation for a list of supported libraries.
+
+## Installing Python Packages
+
+In ToolJet, you can write Python code for custom logic, and for intensive data processing tasks, you can use Python libraries without needing to write complex code from scratch. Here’s how you can use them:
+
+You can use **micropip** to install packages like Pandas and NumPy as follows:
+
+```python
+import micropip
+await micropip.install('pandas')
+await micropip.install('numpy')
+```
+
+Trigger this RunPy query once to install these packages.
+
+
+
+:::tip
+Enable the **Run this query on application load** option in the query settings to make the libraries available throughout the application as soon as the app is loaded.
+:::
+
+## Use Cases
+
+### Parse CSV Data
+
+Let’s say you want users to upload a CSV and view the parsed output. Here’s how you can use pandas and Python’s CSV module. Create a RunPy query to parse CSV data using `StringIO`, `csv`, and `Pandas` module.
+
+```python
+from io import StringIO
+import csv
+import pandas as pd
+
+scsv = components.filepicker1.file[0].content
+
+f = StringIO(scsv)
+reader = csv.reader(f, delimiter=',')
+
+df = pd.DataFrame(reader)
+
+print(df.info())
+print(df)
+```
+
+
+
+- Add a File Picker to your app and change the file type to CSV.
+- In the File Picker’s event settings:
+ - Event: On File Loaded
+ - Action: Run Query → choose your RunPy script
+- Upload a CSV file. When you trigger the RunPy query, it will parse the data and output it in the browser console
+
+### Prompt Preprocessing for AI APIs
+
+When building apps that integrate with AI APIs (like OpenAI, Cohere, or HuggingFace), you often need to send long-form text inputs—like meeting transcripts, user feedback, or document excerpts to the API. However, many AI APIs have input size limitations (e.g., 4,096 tokens for GPT-3.5), and they often work best when the input is clean and concise.
+
+So, before sending the data, you may want to:
+- Clean and normalize the text (remove line breaks, extra spaces, non-ASCII characters)
+- Chunk the text into API-safe sizes (e.g., 500 characters or 300 words)
+- Optionally, remove irrelevant sections (like headers, boilerplate, or disclaimers)
+
+Here's an example of how to do this preprocessing step using regular expressions (`re`):
+
+```python
+import re
+
+# Get raw text from a multi-line input component (like a long form or a textarea)
+raw_text = components.textarea1.text
+
+# 1. Clean the text
+cleaned = re.sub(r"\s+", " ", raw_text).strip()
+
+# 2. Chunk the cleaned text into slices of 500 characters each
+chunks = [cleaned[i:i+500] for i in range(0, len(cleaned), 500)]
+
+# Output the cleaned and chunked data
+print({"chunks": chunks})
+```
+
+
+
+Input - Meeting notes
+
+We discussed the Q3 roadmap and agreed to prioritize performance improvements. There were also suggestions to improve the onboarding experience.
+
+Action items:
+ - Alice will investigate caching issues and report back by next Monday.
+ - Bob will look into UI responsiveness across different screen sizes.
+ - Carol will start planning for the user feedback survey in Q4.
+
+Additional Discussion:
+- A proposal was made to reduce build times by moving to a newer CI/CD system.
+- Concerns were raised about backend API reliability and latency issues.
+- Data team mentioned they are behind on setting up the new dashboard pipeline.
+
+Next Steps:
+- Weekly check-ins will resume starting next Tuesday.
+- Each team will submit a biweekly progress report.
+- Planning for the product demo scheduled for November 15th will start next week.
+
+
+
+
+
+
+Output - Chunked data
+
+```json
+{
+ "chunks": [
+ "We discussed the Q3 roadmap and agreed to prioritize performance improvements. There were also suggestions to improve the onboarding experience. Action items: - Alice will investigate caching issues and report back by next Monday. - Bob will look into UI responsiveness across different screen sizes. - Carol will start planning for the user feedback survey in Q4.",
+
+ "Additional Discussion: - A proposal was made to reduce build times by moving to a newer CI/CD system. - Concerns were raised about backend API reliability and latency issues. - Data team mentioned they are behind on setting up the new dashboard pipeline. Next Steps: - Weekly check-ins will resume starting next Tuesday. - Each team will submit a biweekly progress report. - Planning for the product demo scheduled for November 15th will start next week."
+ ]
+}
+```
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/create-module.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/create-module.md
new file mode 100644
index 0000000000..e11495d51d
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/create-module.md
@@ -0,0 +1,41 @@
+---
+id: create-module
+title: Creating a Module
+---
+
+A module is a reusable interface that can be imported into applications. It allows you to build complex functionality once and reuse it across multiple applications without having to rewrite code each time. This guide explains how to create a module in ToolJet.
+
+Follow these steps to get started with creating a module:
+
+- Go to the **Modules** section from the ToolJet dashboard.
+
+
+
+- Click on the **Create Module** button. In the popup, enter a name for the module.
+
+
+- Add components, queries, and actions just like you would in a normal app. place and resize your components on the **Module container**.
+
+
+
+- Click on the module container to open the properties panel Here you can see the **Input** and **Output** that help in defining how the module communicates with the parent application. These settings define how the module communicates with the parent app, making it easier to build dynamic, reusable modules that work across different data sets and queries.
+
+
+
+
+
+Please refer to the **[Input and Output](/docs/beta/app-builder/modules/input-output)** documentation for detailed information on how to manage the inputs and outputs of a module.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/data-flow.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/data-flow.md
new file mode 100644
index 0000000000..db23999f2c
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/data-flow.md
@@ -0,0 +1,79 @@
+---
+id: data-flow
+title: Data Flow
+sidebar_label: Data Flow
+---
+
+This section explains how data flows work between the parent application and the module.
+
+There are two types of data flows between the parent application and the module:
+- **From Parent to Module**
+- **From Module to Parent**
+
+## From Parent to Module
+
+When you add a module to an app:
+- The parent can pass input values to the module.
+- These values can be used anywhere inside the module (UI, queries, logic).
+
+
+
+For example, let's say you want to pass the **userData** from the parent application to the module. Here's what happens:
+
+```js
+// Passed from parent
+{
+ "userData": {{ queries.getUser.data }}
+}
+```
+You can access these values in the module using the `input` object. For instance, to access the userData, you'd use `{{input.userData}}`.
+
+```js
+// Consumed in module
+{{input.userData}}
+```
+
+## From Module to Parent
+
+The module can send data back to the parent using outputs:
+- Output values are evaluated inside the module.
+- The parent application reads the output values using the components object.
+
+
+
+For example, let's say you have a module that submits a form and sends the submitted data back to the parent app. Here's what happens:
+
+```js
+// Sent from module
+{
+ "submittedFormData": {{ components.form.formData }}
+}
+```
+
+You can access these values in the parent application using the `components` object. For instance, to access the submittedFormData, you'd use `{{components..submittedFormData}}`.
+
+```js
+// Received in parent app
+{{components..submittedFormData}}
+```
+
+## Query Execution Options
+
+You have two options for managing queries in modules:
+
+### Parent-triggered Queries
+- Define queries inside the module.
+- From the parent application, trigger them using module Input query.
+- Use this when you want full control from the app.
+
+
+
+### Self-contained Queries
+- Let the module handle its own queries internally (e.g., run on load or button click inside the module).
+- These queries remain invisible to the parent app.
+- Use this for fully encapsulated behavior.
+
+
+
+Choose based on whether the parent should control the module behavior or let the module manage itself.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/import-export.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/import-export.md
new file mode 100644
index 0000000000..7320518893
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/import-export.md
@@ -0,0 +1,51 @@
+---
+id: import-export-modules
+title: Import and Export Modules
+---
+
+
+ToolJet allows you to export and import modules, making it easy to share, reuse, or migrate modules across different workspaces.
+
+This guide walks you through the steps to export an existing module and import it into another workspace.
+
+## Exporting Modules
+
+- Navigate to the **Modules** tab from the dashboard.
+- Click on the menu icon of the module card you want to export.
+- Click on the **Export module** button.
+
+
+
+- The selected module will be exported as a JSON file.
+
+- This file will include all the components, logic, queries, and properties defined within the module.
+
+Once downloaded, you can use this file to import the module into any other ToolJet workspace.
+
+
+## Importing Modules
+
+- Navigate to the **Modules** tab.
+- Click on the menu icon next to the **Create new module** button in the top right corner.
+
+
+
+
+
+- Choose the module JSON file that you previously exported.
+
+Once imported, the module will appear in your modules list and can be used across your applications.
+
+
+## Module Behavior During Application Import and Export
+
+**Import**:
+
+- When you import an application, the platform automatically checks for any existing modules with matching names in your workspace or instance. If a module with the same name already exists, the imported application connects to the existing module, avoiding duplication.
+- However, if no matching module is found, the platform creates a new module from the imported JSON file.
+- This approach ensures that your application imports smoothly while maintaining consistency and preventing redundant modules.
+
+**Export**:
+
+- When you export an application, all associated modules linked to the application are automatically included in the export.
+- This ensures that any reusable components or features built as modules are preserved and can be seamlessly imported along with the app into any other workspace.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/input-output.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/input-output.md
new file mode 100644
index 0000000000..b10f67c82f
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/input-output.md
@@ -0,0 +1,100 @@
+---
+title: "Configuring Inputs and Outputs"
+id: "input-output"
+---
+
+Modules have their own inputs and outputs which enable them to interact with the parent application. You can configure inputs and outputs of a module from the properties panel.
+
+## Inputs
+Inputs allow the parent application to send data or trigger actions inside the module. You can access inputs in the module using the **input** object.
+
+### Input Types
+- **Data**: Use this to pass values like string, number, boolean, array, or object.
+- **Query**: Use this to trigger queries inside the module from the parent app.
+
+
+
+### How to Define Inputs
+In the properties panel, go to the **Inputs** section and click **Add new**. Then, provide the following:
+
+#### Data Input
+
+For **Data** inputs, define the following parameters:
+
+- **Name**: A unique name for the input
+- **Type**: Select **Data**
+- **Default Value** (optional)
+
+For example, to pass a form title from the parent application, create an input with the name **formTitle** and type **Data**. You can also set a default value.
+
+
+
+To use this input in the module, use the following syntax:
+
+```js
+{{input.}}
+```
+
+For our case, we’ll use `{{input.formTitle}}` to access the form title in the component.
+```js
+{{input.formTitle}}
+```
+
+
+When you import this module into an application, you’ll see the input field in the module settings. If you set the **formTitle** value to **User Details**, the form will display that as the title.
+
+
+
+#### Query Input
+
+For **Query** inputs, define the following parameters:
+- **Name**: A unique name for the input
+- **Type**: Select Query
+
+For example, if you want to trigger a query named **submitForm** from the parent application, create an input named **submit** and select **Query** as the type.
+
+
+
+To handle this input in the module, add an event handler to a component that should trigger the query. Set the action to **Run Query** and select the query as the input (e.g., submit) from the dropdown.
+
+
+
+When you import the module into an applications, you’ll see the query input in the module settings. You can then select any available query in the parent application to be triggered.
+
+
+
+## Testing Inputs
+
+You can test how a module behaves before importing it into an application in the **Test Input** section in the properties panel of the module builder. To do this, open the module's properties panel and scroll to the **Test Input** section. Enter sample values for each input.
+
+For example, if the module has an input named **formTitle**, you can enter a sample value like **User Details** to see how it's rendered in the module.
+
+You can also test query inputs by creating a query inside the module builder and triggering it using the defined query input.
+
+
+
+## Outputs
+
+Outputs allow the module to send data back to the parent app. You can access outputs from the module in the parent application using the components object.
+
+For example, if you want to send submitted form data back to the parent application, create an output named **formData** and pass the form data **From** the component.
+
+
+
+To access this output in the parent application, use the following syntax:
+
+```js
+{{components..}}
+```
+
+In this case, use the following reference to access the form data.
+
+```js
+{{components.formModule.formData}}
+```
+
+
+
+To explore more on how data flows between modules and apps, check out [Data Flow](/docs/beta/app-builder/modules/data-flow) guide.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/overview.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/overview.md
new file mode 100644
index 0000000000..42bcc67146
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/overview.md
@@ -0,0 +1,22 @@
+---
+title: "Overview"
+id: overview
+---
+
+**Modules** in ToolJet are reusable user interfaces that bundle components, queries, actions, and logic. Think of them as mini-apps you can plug into multiple applications within the same workspace. They help eliminate duplication, ensure consistency, and speed up development—especially for repeatable patterns like headers, forms, dashboards, or table views.
+
+Once created, modules can be reused across your workspace. Any updates you make to a module automatically reflect in every app where it's used. This ensures a single source of truth and significantly reduces maintenance effort.
+
+## When to Use Modules
+
+Use modules when:
+
+- You need a shared UI element, like a customer profile or navigation bar, across multiple apps.
+- You’re building repeatable flows such as approval forms, data filters, or input panels.
+- You want to simplify complex logic so others can use it with minimal setup.
+
+Instead of copy-pasting components or logic across apps, modules give you a centralized, reusable way to build features, fully configurable through inputs and outputs to fit any context.
+
+
+
+To get started with modules, check out the [Create Module](/docs/beta/app-builder/modules/create-module) guide. Once your module is built, you can [use it inside any app](/docs/beta/app-builder/modules/use-module) in your workspace.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/use-cases.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/use-cases.md
new file mode 100644
index 0000000000..fc1d6ff5f0
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/use-cases.md
@@ -0,0 +1,67 @@
+---
+id: use-cases
+title: Use Cases
+sidebar_label: Use Cases
+---
+
+Modules let you reuse blocks of functionality across different applications. They help you save time by reducing repetitive work and make app development faster and more consistent.
+
+This guide explains how modules work and showcases common use cases where they can improve your development workflow.
+
+## Common Use Cases for ToolJet Modules
+
+Here are some scenarios where ToolJet Modules are especially useful:
+
+### Reusable User Authentication Form
+
+- Build a user authentication form module using the built-in login block.
+- Add this module to any app that requires user login.
+- Customize the design and behavior as needed for each app.
+
+### Standard Address or Contact Input Block
+
+- Create a reusable module with text fields for address or contact inputs.
+- Use this module in apps that collect user contact details.
+- Adjust the fields and validation rules to match your needs.
+
+### File Uploader with Preview and Validations
+
+- Build a file uploader module using the file upload block.
+- Use it in apps that require file submissions.
+- Set file type restrictions, size limits, and other validations in the module.
+
+### Comment or Feedback Box
+
+- Create a feedback module using the textarea block.
+- Include it in apps where users need to leave comments or feedback.
+- Customize the placeholder, character limit, and other properties.
+
+### Order Summary with Pricing Logic
+
+- Build an order summary module using the table block.
+- Add it to apps that need to show order details and pricing.
+- Configure columns, data sources, and styles within the module.
+
+### Notification Banner with Custom Message
+
+- Create a notification banner module using the alert block.
+- Use it in apps that need to show alerts or system messages.
+- Customize the text, colors, and display duration.
+
+### Pagination and Filter Toolbar
+
+- Build a pagination and filter toolbar module using buttons and dropdowns.
+- Use it in apps that display paginated or filtered lists.
+- Customize the layout, filters, and button actions.
+
+### Embedded Chart with Dynamic Data
+
+- Create a chart module using the built-in chart block.
+- Use it in apps that need to visualize data.
+- Pass dynamic datasets to the module via inputs or variables.
+
+### Table with Sorting, Filtering, and Export
+
+- Build a table module with sorting, filtering, and export features.
+- Add it to apps that display structured data.
+- Customize functionality to fit your data and user needs.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/using-module.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/using-module.md
new file mode 100644
index 0000000000..e8d03c6d0d
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/modules/using-module.md
@@ -0,0 +1,28 @@
+---
+title: "Using Modules"
+id: "using-modules"
+---
+
+Once a module is created, it becomes available in the **Module** section of the component panel inside the App-Builder. You can use it like any other component by dropping it on the canvas and configuring it.
+
+
+### Steps to Use a Module:
+
+1. Open the application in which you want to use a module.
+
+2. In the component library panel, switch to the **Module** section.
+
+3. **Drag and drop** the Module onto the canvas.
+
+
+
+5. You can select the module, to see a list of required **inputs** (if any) defined in the module.
+
+6. Bind the inputs to values from your data source or configure static values if needed.
+
+7. If your module has **outputs**, you can reference them using:
+ ```js
+ {{components..}}
+ ```
+
+You can reuse the same module multiple times in a single application by dropping it multiple times on the canvas and configuring each instance with different input bindings.
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/overview.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/overview.md
new file mode 100644
index 0000000000..4c56833857
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/overview.md
@@ -0,0 +1,76 @@
+---
+id: overview
+title: Overview
+---
+
+ToolJet’s app-builder offers an AI-native, low-code environment that helps you build and deploy internal tools quickly without extensive coding knowledge. Whether it’s dashboards, approval workflows, tracking systems, or scheduling apps, you can create powerful tools in minutes.
+
+Teams across engineering, product, operations, and business can build applications with ease, following four simple steps to get up and running in minutes.
+
+1. **Build the Interface** – Design visually with drag-and-drop components.
+
+2. **Connect Your Data** – Integrate with databases, APIs, and third-party services.
+
+3. **Make It Interactive** – Add actions, events, and logic to bring your application to life.
+
+4. **Handle Complex Logic** – Use JavaScript anywhere for advanced workflows.
+
+In this guide, you’ll walk through each step and see how they fit together to bring your application to life.
+
+
+
+
+## 1. Building the Interface
+
+Start designing your application’s interface with 60+ pre-built components, from **Tables** and **Forms** to **Charts** and **Buttons**. Just drag and drop components onto the canvas, resize, reposition, and fine-tune them through the Properties Panel.
+
+
+
+Each component comes with built-in styling options. You can customize text, colors, visibility, and more through the Style Panel. These components are dynamic, allowing you to manage state and events just like you would in your favorite frontend framework.
+
+## 2. Connecting Your Data
+
+You can connect your application to multiple [data sources](/docs/data-sources/overview) including SQL, NoSQL, vector databases, APIs, spreadsheets, and cloud services. Once connected, you can fetch, update, or manipulate data using queries.
+
+A query is an action that interacts with your data source, whether it's fetching records, filtering results, or writing data. It acts as the bridge between your UI and your data.
+
+
+
+
+Use the **Query Panel** to build queries, either with a form-based interface or by writing code/SQL directly. You can use them to fetch data to display in components or to push user inputs back to your database. They can run manually or be triggered using events like page load, user actions on components, or the success or failure of other queries.
+
+## 3. Making Apps Interactive
+
+Make your apps interactive by adding events to components, queries, and pages. Events define how your application responds to user actions or specific conditions, bringing interactivity to your application. ToolJet provides a declarative Events system, similar to JavaScript event handlers, allowing you to control application behavior without writing repetitive code.
+
+
+
+Events can be triggered by various actions such as a button click, form submission, page load, or the completion of a query. When triggered, you can execute actions like running a query, opening a modal, showing a notification, or navigating to another page.
+
+You can also chain multiple events and actions together, enabling complex multi-step workflows without writing boilerplate code.
+
+## 4. Handling Custom Logic
+
+ToolJet makes it easy to build apps without code, but when you need more control, it offers the ability to add custom code to write your logic. You can create JavaScript or Python queries in application builder to perform calculations, transform data, trigger other queries, or update UI components. These snippets have full access to the component’s properties, other queries’ outputs, and the entire app’s state, allowing you to write custom logic for any use cases like conditional behavior, data processing, or dynamic UI updates.
+
+
+
+This gives developers the ability to handle complex scenarios with code, while still leveraging ToolJet’s low-code environment.
+
+## Use Cases
+With ToolJet, you can build a wide range of internal tools including but not limited to:
+
+* Inventory management system
+* Purchase order tracker
+* Customer onboarding portal
+* Loan origination and underwriting tool
+* Fraud detection dashboard
+* Expense approval workflow
+* Timesheet tracker
+* Employee onboarding app
+* Internal ticketing system
+* Compliance reporting dashboard
+* Sales pipeline tracker
+* Digital asset management portal
+
+From simple dashboards to complex data-driven tools, ToolJet's app-builder helps you build applications with speed and precision.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/app-builder/walkthrough/row-level-security.md b/docs/versioned_docs/version-3.16.0-LTS/app-builder/walkthrough/row-level-security.md
new file mode 100644
index 0000000000..a1c8c0dbae
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/app-builder/walkthrough/row-level-security.md
@@ -0,0 +1,61 @@
+---
+id: row-level-security
+title: Setup Row Level Security
+---
+
+Row-level security in ToolJet lets you control which records a user can see or interact with, even when multiple users access the same table. This is useful when you want to restrict access to specific rows based on [custom groups](/docs/user-management/role-based-access/custom-groups/) or [default user roles](/docs/user-management/role-based-access/user-roles#default-user-roles). Row-level security is applied on the server side, ensuring the logic is secure and hidden from the client.
+
+The below syntax fetches the groups for the current user from the server side. Groups include both custom groups and default user roles like `admin` and `end-user`.
+
+```bash
+{{globals.server.currentUser.groups}}
+```
+
+*The above syntax will work with all data sources except Run Javascript and Run Python.*
+
+## Example: Department-Specific View Using a PostgreSQL Data Source
+
+If you're using PostgreSQL, you can filter records by referencing the user’s group(s) directly in your SQL query. This ensures each user only sees data relevant to them.
+
+Suppose you're building an internal issue tracking tool for your company. Each department (like “Engineering”, “HR”, "Marketing") logs and manages its own issues in a shared table with the below structure:
+
+- Table name: **issue_reports**
+- Columns: **id**, **title**, **status** and **department**
+- Access control: Each user is assigned to department-based Custom Groups matching department names in the database.
+
+To ensure users only see reports from their own department(s), you can use the following SQL query:
+
+```SQL
+ SELECT * FROM issue_reports
+ WHERE department = ANY (
+ string_to_array('{{globals.server.currentUser.groups}}', ',')
+ );
+```
+
+**How This Works:**
+- `{{globals.server.currentUser.groups}}` fetches the user’s groups securely from the server.
+- `string_to_array(...)` converts the comma-separated string containing groups into a usable array.
+- `department = ANY (...)` ensures users only see issues filed under their own departments.
+
+### Filtered Results Based on Departments:
+
+Based on the query logic, users assigned to the Engineering and HR groups will see the following issues:
+
+| id | title | status | department |
+|:---|:----------------------------------|:---------|:------------|
+| 1 | Login bug on portal | Open | Engineering |
+| 3 | Leave approval stuck | Open | HR |
+| 4 | Data sync error | Open | Engineering |
+| 5 | Employee onboarding delay | Pending | HR |
+| 9 | GitHub webhook failure | Open | Engineering |
+
+Users assigned to the Marketing group will see only the issues related to their department:
+
+| id | title | status | department |
+|:---|:----------------------------------|:---------|:---------------|
+| 2 | Deliver failure issues | Pending | Marketing |
+| 7 | Campaign budget approval delayed | Pending | Marketing |
+| 8 | Social media calendar not updated | Open | Marketing |
+
+This setup ensures that a shared internal tool remains secure, with minimal query changes and no duplication of logic or views, making it ideal for HR dashboards, ticketing systems, CRM tools, and more.
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/ai-docs-assitant.md b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/ai-docs-assitant.md
new file mode 100644
index 0000000000..47f52240b9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/ai-docs-assitant.md
@@ -0,0 +1,56 @@
+---
+id: ai-docs-assistant
+title: AI Docs Assistant
+---
+
+The **AI Docs Assistant** is an intelligent assistant designed to help you navigate ToolJet’s documentation with ease. Whether you need quick answers, step-by-step guides, or concept explanations, this assistant provides instant support by summarizing docs, troubleshooting issues, and guiding you through best practices.
+
+You can access the AI Docs Assistant under the **Learn** tab in the Build with AI sidebar. Simply ask a question or describe what you're looking for, and the assistant will provide relevant documentation and insights.
+
+
+
+
+
+
+
+### What You Can Do with the AI Docs Assistant
+- Get guidance on setting up workspaces, managing users, and configuring roles.
+- Quickly understand complex topics with clear, actionable steps
+- Find answers to common questions and resolve issues efficiently.
+- Explore how to connect databases, APIs, and external tools.
+- Discover best practices for securing your applications.
+- Get up to speed with key functionalities and platform best practices.
+
+### Examples
+
+1. Custom Schema in [Form](/docs/widgets/form/) component:
+
+ **Prompt**: Can you create a custom schema for a Form component with two input fields for name, phone number, and a dropdown for gender?
+
+
+
+
+
+
+
+
+ 2. Dynamic columns in [Table](/docs/widgets/table/table-properties/) component:
+
+ **Prompt**: Can you explain dynamic columns in the Table component?
+
+
+
+
+
+
+
+
+ 3. Plotly JSON in [Chart](/docs/widgets/chart/) component:
+
+ **Prompt**: Can you help me with the structure of the Plotly JSON that I can pass into Chart components?
+
+
+
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/generate-applications.md b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/generate-applications.md
new file mode 100644
index 0000000000..dd676660c9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/generate-applications.md
@@ -0,0 +1,58 @@
+---
+id: generate-applications
+title: Generate Applications
+---
+
+This guide explains how to quickly generate and modify business applications using ToolJet. You can create an app from scratch with a single prompt or enhance an existing app with AI-powered assistance.
+
+## Creating Application
+To create an application, follow these steps:
+
+1. **Enter a prompt** – Describe the business application you want to build in the prompt input on the dashboard.
+
+
+
+
+
+
+2. **Accept or modify requirements** – After submitting your prompt, the app will be created, and you’ll be taken to the App Builder, where a list of features, a database schema, design details, and query specifications will be generated based on your prompt.
+
+
+
+
+
+
+You can accept or modify these application requirements after reviewing them thoroughly.
+
+
+
+
+
+
+3. **App Generation** – Once you confirm the requirements, ToolJet will build the application inside the App Builder.
+
+
+
+
+
+
+
+## Modifying Application
+
+You can modify any application in ToolJet with AI assistance, whether it's a newly created app or an existing one. You can update components and queries within your application with just a prompt.
+
+For example, if you want to add a button in your app you can write a prompt for the same.
+
+
+
+
+
+
+## Limitations
+
+ToolJet supports generating queries with AI for the following data sources:
+
+- [Postgres](/docs/data-sources/postgresql/)
+- [MySQL](/docs/data-sources/mysql/)
+- [SQL Server](/docs/data-sources/mssql/)
+- [RunJS Queries](/docs/data-sources/run-js)
diff --git a/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/overview.md b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/overview.md
new file mode 100644
index 0000000000..e1118e3db9
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/overview.md
@@ -0,0 +1,40 @@
+---
+id: overview
+title: Overview
+---
+With ToolJet, you can build business applications effortlessly using natural language. Whether you're starting from scratch or refining an existing app, it simplifies the process with it's intelligence.
+
+Additionally, it comes with an AI-powered [documentation assistant](/docs/build-with-ai/ai-docs-assistant), ready to answer any questions about ToolJet's features, components, and integrations, helping you build faster.
+
+Follow these step-by-step instructions to create an inventory management application:
+1. **Describe your application** – Provide a prompt detailing the business application you want to create. (Example: "Inventory management system for a manufacturing company.")
+
+
+
+
+
+2. **Refine the requirements** – Review and accept or modify the application requirements suggested.
+
+
+
+
+
+3. **Customize your application** – Use AI to customize the generated application to your specific needs, adjusting components and styles, and also data source queries.
+
+- **Generated Application**
+
+
+
+
+
+
+- **Customizing Application**
+
+
+
+
+
+
+
+
+Refer to [Generate Applications](/docs/build-with-ai/generate-applications) and [AI Docs Assistant](/docs/build-with-ai/ai-docs-assistant) documentation to learn more.
diff --git a/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/tooljet-mcp.md b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/tooljet-mcp.md
new file mode 100644
index 0000000000..979530422c
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/build-with-ai/tooljet-mcp.md
@@ -0,0 +1,300 @@
+---
+id: tooljet-mcp
+title: ToolJet MCP
+
+---
+
+
+ Self Hosted
+
+The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a standard for connecting Large Language Models (LLMs) to platforms like ToolJet. This guide covers how to connect ToolJet to AI tools using MCP, enabling your AI assistants to interact with and manage your ToolJet instance.
+
+## What is ToolJet MCP?
+
+ToolJet MCP is a bridge that connects AI assistants to your ToolJet platform through the Model Context Protocol. This allows AI tools to:
+
+- Manage users and workspaces
+- Access app information
+- Perform administrative tasks
+- Interact with your ToolJet instance programmatically
+
+## Supported AI Tools
+
+You can connect ToolJet to the following AI tools using MCP:
+
+- [Cursor](#cursor)
+- [Windsurf](#windsurf) (Codium)
+- [Visual Studio Code](#visual-studio-code-copilot) (Copilot)
+- [Cline](#cline) (VS Code extension)
+- [Claude desktop](#claude-desktop)
+- [Claude code](#claude-code)
+
+## Prerequisites
+
+Before you begin, you'll need:
+
+1. A ToolJet instance with admin access
+2. An API access token from your ToolJet instance
+3. Node.js (v14 or higher)
+4. An MCP-compatible AI assistant
+
+## Getting Started
+
+### Step 1: Get an Access Token
+
+Get an access token from your ToolJet instance. You'll need this token to authenticate the MCP server. Refer to the [ToolJet API](https://docs.tooljet.ai/docs/tooljet-api#enabling-tooljet-api) documentation for more details on how to generate an API token.
+
+### Step 2: Configure Your AI Tool
+
+Follow the instructions below to configure your preferred AI tool to connect with ToolJet MCP.
+
+#### Cursor
+
+1. Open [Cursor](https://www.cursor.com/) and create a `.cursor` directory in your project root if it doesn't exist.
+2. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
+3. Add the following configuration:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+Replace `` with your ToolJet access token and update the host URL to point to your ToolJet instance.
+
+4. Save the configuration file.
+5. Open Cursor and navigate to **Settings/MCP**. You should see a green active status after the server is successfully connected.
+
+#### Windsurf
+
+1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
+2. Tap on the hammer (MCP) icon, then **Configure** to open the configuration file.
+3. Add the following configuration:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+Replace `` with your ToolJet access token and update the host URL to point to your ToolJet instance.
+
+4. Save the configuration file and reload by tapping **Refresh** in the Cascade assistant.
+5. You should see a green active status after the server is successfully connected.
+
+#### Visual Studio Code (Copilot)
+
+1. Open [VS Code](https://code.visualstudio.com/) and create a `.vscode` directory in your project root if it doesn't exist.
+2. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
+3. Add the following configuration:
+
+```json
+{
+ "inputs": [
+ {
+ "type": "promptString",
+ "id": "tooljet-access-token",
+ "description": "ToolJet access token",
+ "password": true
+ },
+ {
+ "type": "promptString",
+ "id": "tooljet-host",
+ "description": "ToolJet host URL",
+ "default": "https://your-tooljet-instance.com"
+ }
+ ],
+ "servers": {
+ "tooljet": {
+ "command": "npx",
+ "args": ["-y", "@tooljet/mcp"],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "${input:tooljet-access-token}",
+ "TOOLJET_HOST": "${input:tooljet-host}"
+ }
+ }
+ }
+}
+```
+
+4. Save the configuration file.
+5. Open Copilot chat and switch to "Agent" mode. You should see a tool icon that you can tap to confirm the MCP tools are available. Once you begin using the server, you will be prompted to enter your access token and host URL.
+
+For more info on using MCP in VS Code, see the [Copilot documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers).
+
+#### Cline
+
+1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
+2. Tap **Configure MCP Servers** to open the configuration file.
+3. Add the following configuration:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+Replace `` with your ToolJet access token and update the host URL to point to your ToolJet instance.
+
+4. Save the configuration file. Cline should automatically reload the configuration.
+5. You should see a green active status after the server is successfully connected.
+
+#### Claude desktop
+
+1. Open [Claude desktop](https://claude.ai/download) and navigate to **Settings**.
+2. Under the **Developer** tab, tap **Edit Config** to open the configuration file.
+3. Add the following configuration:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+Replace `` with your ToolJet access token and update the host URL to point to your ToolJet instance.
+
+4. Save the configuration file and restart Claude desktop.
+5. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
+
+#### Claude code
+
+1. Create a `.mcp.json` file in your project root if it doesn't exist.
+2. Add the following configuration:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+Replace `` with your ToolJet access token and update the host URL to point to your ToolJet instance.
+
+3. Save the configuration file.
+4. Restart [Claude code](https://claude.ai/code) to apply the new configuration.
+
+## Platform-Specific Setup
+
+### Windows Users
+
+If you're using Windows, prefix the command with `cmd /c`:
+
+```json
+{
+ "mcpServers": {
+ "tooljet": {
+ "command": "cmd",
+ "args": [
+ "/c",
+ "npx",
+ "-y",
+ "@tooljet/mcp",
+ ],
+ "env": {
+ "TOOLJET_ACCESS_TOKEN": "",
+ "TOOLJET_HOST": "https://your-tooljet-instance.com"
+ }
+ }
+ }
+}
+```
+
+## Available Tools
+
+ToolJet MCP provides several tools that AI assistants can use to interact with your ToolJet instance:
+
+### User Management
+
+| Tool | Description |
+| --- | --- |
+| `get-all-users` | Retrieve a list of all users in your ToolJet instance |
+| `get-user` | Get detailed information about a specific user |
+| `create-user` | Create a new user in a specified workspace |
+| `update-user` | Update a user's profile information |
+| `update-user-role` | Change a user's role within a workspace |
+
+### Workspace Management
+
+| Tool | Description |
+| --- | --- |
+| `get-all-workspaces` | List all workspaces in your ToolJet instance |
+
+### Application Management
+
+| Tool | Description |
+| --- | --- |
+| `get-all-apps` | List all applications within a specific workspace |
+
+## Example Usage
+
+Once connected, your AI assistant can perform tasks like:
+
+- "Show me all users in my ToolJet instance"
+- "Create a new user named John Doe in the Marketing workspace"
+- "List all the apps in the Development workspace"
+- "Update the role of user@example.com to Admin in the Sales workspace"
+
+
+For a full list of tools available, see the [GitHub README](https://github.com/ToolJet/tooljet-mcp). If you experience any issues, [submit a bug report](https://github.com/ToolJet/tooljet-mcp/issues/new).
+
+
+
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/_category_.json b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/_category_.json
new file mode 100644
index 0000000000..317067020d
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Contributing Guide",
+ "position": 11,
+ "collapsed": true
+}
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/code-of-conduct.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/code-of-conduct.md
new file mode 100644
index 0000000000..03f7184d51
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/code-of-conduct.md
@@ -0,0 +1,81 @@
+---
+id: code-of-conduct
+title: Contributor Code of Conduct
+---
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies when
+an individual is representing the project or its community in public spaces.
+Examples of representing a project or community include using an official
+project e-mail address, posting via an official social media account, or acting
+as an appointed representative at an online or offline event. Representation of
+a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at hello@tooljet.com . All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/introduction.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/introduction.md
new file mode 100644
index 0000000000..e574a4f7b8
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/introduction.md
@@ -0,0 +1,40 @@
+---
+id: introduction
+title: Introduction
+---
+
+Welcome to the ToolJet Documentation guide! We're thrilled to have you here and can't wait to see the contributions you'll make to our docs. Your insights and improvements will help thousands of users get the most out of ToolJet 🚀.
+
+In the following sections, you'll find everything you need to get started with contributing to our documentation.
+
+## What Makes Good Documentation?
+
+Good documentation is all about clarity, simplicity, and effectiveness. It should guide users through the usage and workings of ToolJet with precision, making it as easy as possible for them to build and deploy internal tools.
+
+Documentation is a cornerstone of our product. It's one of the most visited sections of our website, second only to our homepage. Without well-crafted, thorough documentation, users can't fully utilize the power of ToolJet, and their experience may suffer as a result.
+
+### The Golden Rule
+
+If a ToolJet feature lacks comprehensive documentation, it isn't considered complete. Quality documentation is essential for the success of our product and the satisfaction of our users. It’s often through documentation that users discover new features or decide to upgrade their tools.
+
+As a contributor, you can create detailed, user-centric content that thoroughly covers the features you're documenting in an engaging and accurate way.
+
+## Types of Documentation
+
+We aim to create rich, informative content across three key areas of documentation:
+
+1. **ToolJet Concepts**: Basic explanations of specific topics, offering general insights into how ToolJet features work.
+2. **How-to Guides**: Step-by-step instructions that help users accomplish specific tasks within ToolJet.
+3. **Reference**: Concise, lookup-style documentation that covers all possible usages, definitions, and edge cases for ToolJet features.
+
+As you document features, consider which of these categories your content fits best. Sometimes a feature might require multiple types of documentation to be fully covered.
+
+### Measuring Impact and Gathering Feedback
+
+The quality of our documentation isn't just determined by how well it's written—it's also measured by how well it serves our users. We regularly use analytics and feedback tools to assess the impact of our documentation and make data-driven improvements.
+
+## Next Steps...
+
+Once you've set up your local environment, take some time to explore our [Style Guide](/docs/contributing-guide/documentation-guidelines/style-guide), understand our page structures, and learn how to work with Docusaurus, the framework we use for our documentation.
+
+We look forward to your contributions and are excited to see how you'll help make ToolJet documentation even better!
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/pr-checklist.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/pr-checklist.md
new file mode 100644
index 0000000000..1d3fe3a7cb
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/pr-checklist.md
@@ -0,0 +1,15 @@
+---
+id: pr-checklist
+title: Pull Request Review Checklist
+---
+
+Use this quick checklist to catch common issues and maintain consistency across your project.
+
+- Review spelling and grammar using tools like ChatGPT or Grammarly.
+- Ensure no compilation issues arise due to formatting.
+- Verify that all h2 and h3 headings follow title case.
+- Confirm that every section starting with h2 has a 24px padding-top.
+- Test for broken links, missing images, and any incorrect code.
+- Ensure new internal links use root-relative file paths.
+- Identify opportunities for relevant cross-linking.
+- Ensure that the changes are implemented in all the required versions.
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/style-guide.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/style-guide.md
new file mode 100644
index 0000000000..65f70547da
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/documentation-guidelines/style-guide.md
@@ -0,0 +1,236 @@
+---
+id: style-guide
+title: Style Guide
+---
+
+Welcome to the ToolJet's Style Guide for creating clear, consistent, and accessible documentation. In this guide, you will find recommendations on text formatting, proper use of headers, code snippet styling, accessibility practices, and much more.
+
+## 1. Text Formatting Guidelines
+
+Different elements in your projects should be formatted consistently for clarity. Here are some recommendations:
+
+a. Italics are used for names given to Queries, Database Tables, and Components.
+
+**Examples:**
+- Create a new query and rename it to *getEmployees*.
+- Select **ToolJetDB** as the the data source and *Employees* table as the data source.
+- Pass the returned data to the *allEmployees* component.
+
+b. Bold is applied for Workspace Constants, Clickable Buttons, fx, Data Sources, and Components.
+
+**Examples:**
+- Select the **Button** component and change its label to "Save".
+- Drag andn drop a **Table** component and rename it to *todosTable*.
+- Expand the query panel at the bottom and click on the **Add** button to create a new **REST API** query.
+
+
+c. Use Single Ticks for Inline Code and Triple Ticks for Multi-Line Code.
+
+**Examples:**
+- The **fx** option next to the Loading state property can be used to add a loader to the component. For instance, you can enter `{{queries.getData.isLoading === true}}` to show the loader while the *getData* query is running.
+- Use the below code to fetch data:
+```js
+// this code is wrapped in triple ticks
+const fetchData = async () => {
+const response = await api.get('/users');
+console.log(response.data);
+};
+```
+
+**Additional Items**:
+- API Endpoints: Use code ticks for API endpoints (e.g., `GET /api/v1/resources`).
+- Labels or User Inputs: Use double quotes to highlight labels or user inputs (e.g., "Enter your username").
+
+---
+
+## 2. Headings
+
+Proper use of headers is crucial for organizing content and improving readability. Use the following guidelines to determine which header level to apply:
+
+- **Title Casing**: Apply title casing for all headers to maintain consistency.
+- **Main Header**: Use a single hash (`#`) for the main topic of the document or section. This should be used once per document for the main header.
+- **Secondary Header**: Use double hashes (`##`) for subtopics or main sections within a major section. This level of header should organize content under the main header.
+- **Tertiary Header**: Use triple hashes (`###`) for more detailed points or subsections under a secondary header. This header is useful for going deeper into specifics within a section.
+- **Quaternary Header**: Use four hashes (`####`) for even more granular details within a tertiary section. This header is rarely needed but can be useful in complex documentation.
+- **Spacing**: Ensure there’s a blank line before and after each header to maintain readability and to separate the sections clearly.
+- **Header Frequency**: Avoid using more than three levels of headers to prevent overcomplication. If additional granularity is needed, consider breaking the content into separate sections or documents.
+
+---
+
+## 3. Markdown Tables
+
+To efficiently present extensive and repetitive information about features, such as the properties of a component, use markdown tables. This format helps organize and display the data clearly and concisely.
+
+Ensure all tables are left-aligned for consistency. This aids in readability and ensures that the content is easy to scan.
+
+**Example**:
+|
Variable
|
Description
|
How To Access
|
+|:---------- | :---------- | :------------ |
+| chartTitle | Holds the title of the chart component. | Accessible dynamically with JS (for e.g., `{{components.chart1.chartTitle}}`). |
+| xAxisTitle | Contains the title for the X-axis of the chart. | Accessible dynamically with JS (for e.g., `{{components.chart1.xAxisTitle}}`). |
+| yAxisTitle | Contains the title for the Y-axis of the chart. | Accessible dynamically with JS (for e.g., `{{components.chart1.yAxisTitle}}`). |
+| clickedDataPoints | Stores details about the data points that were clicked.| Accessible dynamically with JS (for e.g., `{{components.chart1.clickedDataPoints}}`). Each data point includes `xAxisLabel`, `yAxisLabel`, `dataLabel`, `dataValue`, and `dataPercent`. |
+
+- Use **bold** formatting for all column headers to differentiate them from the table content.
+- Avoid leaving empty cells in tables. If a cell doesn’t have applicable content, use a placeholder like "N/A" or "—" to indicate that the cell is intentionally blank.
+
+---
+
+## 4. Admonitions
+
+Admonitions are blocks of content that are designed to draw attention to specific points in your documentation. Use them sparingly to avoid overwhelming the user. Reserve admonitions for critical or cautionary information only.
+
+- **Warning Admonitions**: Use `warning` type admonitions for high-risk actions or irreversible changes. This type of admonition should alert users to potential dangers or critical issues.
+
+**Example**:
+:::warning
+Ensure you back up your data before upgrading to the latest version.
+:::
+
+- **Tip Admonitions**: Use `info` type admonitions to offer useful hints or best practices. These are generally positive and provide additional value to the user.
+
+**Example**:
+:::info
+Preview the changes before pushing them.
+:::
+
+Overuse can dilute their impact. Use *italics* instead of admonitions whenever possible to emphasize important information instead of admonitions. This is a less intrusive way to draw attention to key details.
+
+---
+
+## 5. Image Guidelines
+Include images that closely align with real-world use cases. This makes the documentation more practical and relatable for the user.
+
+- Name images to reflect their purpose, such as `create-get-query.jpeg`. This helps maintain an organized file structure and makes it easier to locate specific images.
+- Align images to the left. This is the standard alignment that works well with most content layouts.
+- Set the image width to 100% to ensure it scales appropriately with different screen sizes.
+- Keep image sizes under 300kb to balance load speed and quality.
+- Alt text should be a concise description of the image, providing the same information as the image itself. This is essential for accessibility and for users who rely on screen readers.
+- Skip phrases like "image of" or "graphic of" as screen readers handle this automatically. Focus on describing what is important about the image.
+- Use `WEBP` or `PNG` formats for web images due to their balance between quality and file size.
+- Use `SVG` for logos or icons to ensure scalability without loss of quality.
+
+---
+
+## 6. Tone and Clarity
+
+Maintaining a clear and consistent tone throughout your documentation is crucial for effective communication. The goal is to be concise, informative, and user-friendly.
+
+- Keep language straightforward and concise. Avoid jargon unless it's essential for the audience and provide explanations where necessary.
+- Always proofread content using Grammarly or a similar tool before submitting a PR. This helps catch errors that might be missed during the initial writing process.
+- Use the active voice wherever possible to make the content more direct and engaging. Passive voice can make sentences longer and more difficult to understand.
+
+
+---
+
+## 7. Bullet Points
+
+Use bullet points to break down steps or lists for clarity. This makes the content easier to scan and understand.
+
+- Avoid using bullet points for a single item. If there is only one point to make, integrate it into the main text instead.
+- Ensure subpoints are correctly indented in markdown. This maintains the hierarchy and relationship between the main point and subpoints.
+- End bullet points that are complete sentences with a period. This helps maintain proper grammar and readability.
+- Do not insert blank lines between bullet points. This keeps the list compact and visually connected.
+- Use nested bullet points for items that require further explanation or hierarchy within a list.
+
+---
+
+## 9. Specific Language Guidelines
+
+Use the below language guidelines to ensure clarity and consistency.
+
+### HTTP Formatting
+
+- All HTTP headers should be capitalized like this: `First-Letter-Capitalized`. This follows the standard convention and makes the headers easier to distinguish.
+ **Example**:
+```
+Content-Type: application/json
+Authorization: Bearer
+```
+
+- HTTP blocks should be ready to run when pasted into tools like Postman or `cURL` commands. This means including all necessary components like headers, body, and method. **Example**:
+```bash
+curl -X POST https://api.example.com/resource \
+-H 'Content-Type: application/json' \
+-H 'Authorization: Bearer ' \
+-d '{"key": "value"}'
+```
+
+### JavaScript Guidelines
+
+- End statements with semicolons (`;`). While JavaScript can often infer semicolons, explicitly including them prevents potential issues, especially in complex code. **Example**:
+```javascript
+const name = 'John';
+console.log(name);
+```
+
+- Use single quotes for strings unless double quotes are necessary (e.g., to avoid escaping single quotes inside the string). **Example**:
+```javascript
+const greeting = 'Hello, world!';
+```
+
+### JSON Formatting
+
+- Indent JSON by 2 spaces. This is a standard practice that improves readability. **Example**:
+```json
+{
+ "name": "John Doe",
+ "age": 30,
+ "city": "New York"
+}
+```
+
+- Avoid comments in JSON code, as JSON does not natively support comments. If explanations are needed, provide them outside the JSON block in the documentation.
+
+### Shell Scripting
+
+- Break separate commands into distinct code blocks or chain them with `&&` for readability. For multi-line commands, use `\` to break lines. **Example**:
+```bash
+sudo apt-get update && \
+sudo apt-get install -y curl
+```
+
+- Preface comments with `#` to explain the command's purpose.
+**Example**:
+```bash
+# This command installs Node.js
+sudo apt-get install -y nodejs
+```
+
+### SQL Queries
+
+- Format SQL queries with keywords in uppercase, and break down long queries into multiple lines for better readability. **Example**:
+```sql
+SELECT name, age, city
+FROM users
+WHERE age > 30
+ORDER BY name ASC;
+```
+
+---
+
+## 10. Linking Guidelines
+
+- Use root-relative paths (e.g., `/schema/postgres/tables.mdx`) instead of relative links to avoid broken links during file moves. This practice ensures that links remain functional even if files are moved within the directory structure. **Example**:
+`[Postgres tables](/schema/postgres/tables.mdx)` links to the Postgres tables page.
+
+- When linking to a specific section within a page, use anchor links to direct the user precisely where needed. **Example**:
+`ToolJet supports [multiple environments,](https://docs.tooljet.ai/docs/#multiple-environments)` takes the user directly to the specific section.
+
+
+---
+
+## 11. Semantics and Terminology
+
+- Write in the second person (e.g., *you*, *your*). This makes the content more engaging and directly applicable to the reader.
+- Ensure that case sensitivity is consistently applied across the document, particularly for technical terms or commands. This is important for commands and variables in code that are case-sensitive.
+**Example**: "`MyVariable` and `myvariable` are not the same."
+- Define acronyms on first use and avoid using them excessively to maintain readability. This helps readers who may not be familiar with all acronyms.
+**Example**: "The Content Delivery Network (CDN) is used to deliver content to users efficiently."
+- Maintain consistent terminology throughout the document. If you start with "user," don't switch to "customer" later in the same context.
+
+---
+
+
+
+By following these guidelines, you can ensure that your documentation is clear, consistent, and easy to use for a wide range of audiences.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/l10n.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/l10n.md
new file mode 100644
index 0000000000..cb74a19611
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/l10n.md
@@ -0,0 +1,69 @@
+---
+id: l10n
+title: Localization
+---
+
+Welcome to ToolJet Localization Guide. The goal of the Localization is to make ToolJet easy to use and close to all countries, languages, and general cultural groups. On this page, you will find instructions on how to contribute to ToolJet through Localization and make a more friendly ToolJet for all regions.
+
+## Adding Translations
+
+- For adding the translations of your language in ToolJet, you'll need to create a new **languagecode.json** file which will include all the translations for the keywords in your language, and then list the language in the **languages.json** file for the language to be listed in the dashboard of the ToolJet.
+
+- Go to the **frontend** directory which is at the root of ToolJet, then go to the **assets** and inside assets, you'll find the **translations** directory. You have created a new json file with the **language code** as the file name. The language code should follow [ISO 639-1 Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
+
+ ```
+ \frontend
+ |--\assets
+ |--\--\translations
+ |--\--\--\languages.json
+ |--\--\--\en.json
+ ```
+
+
+
+
+
+
+
+- Let's localize ToolJet in the **French** language. Create a new json file inside the translations directory and name it **fr.json**. `fr` is the language code for French.
+
+- After creating the new file, open the **en.json** file and copy all the contents of the file to the newly created **fr.json**.
+
+
+
+
+
+
+
+- Once copied, you can now start adding the translations for the keywords in the french language.
+
+- After completing the translation, all you need to do is list the language in **languages.json** file. You'll need to add an object with three key-value pairs. **lang** - the name of the language that you added, **code** - the language code, and the **nativeLang** - name of language in the native.
+
+ ```js
+ {
+ "languageList":
+ [
+ { "lang": "English", "code": "en", "nativeLang": "English" },
+ { "lang": "French", "code": "fr", "nativeLang": "Français" }
+ ]
+ }
+ ```
+
+
+
+:::note
+Feel free to reach us on [Slack](https://join.slack.com/t/tooljet/shared_invite/zt-2rk4w42t0-ZV_KJcWU9VL1BBEjnSHLCA) for any help related to Localization.
+:::
diff --git a/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/marketplace/creating-a-plugin.md b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/marketplace/creating-a-plugin.md
new file mode 100644
index 0000000000..a7a9cdf67b
--- /dev/null
+++ b/docs/versioned_docs/version-3.16.0-LTS/contributing-guide/marketplace/creating-a-plugin.md
@@ -0,0 +1,424 @@
+---
+id: creating-a-plugin
+title: 'Marketplace: Creating plugins'
+---
+
+## What are plugins
+
+ToolJet’s development has centered on extensibility, allowing developers to utilize plugins that expand their capabilities. Currently, these plugins are limited to connectors, including data source connectors like PostgreSQL, MySQL, Twilio, Stripe, and more. Using JavaScript/TypeScript, developers can develop plugins to enhance ToolJet's functionality and publish these plugins on the ToolJet Marketplace.
+
+This guide will provide step-by-step instructions for creating ToolJet plugins using the `tooljet` CLI.
+
+The `tooljet` CLI is a user-friendly command-line tool designed to simplify the plugin building process. As part of this guide, we will create a basic plugin for GitHub.
+
+## Step 1: Creating a New Plugin - GitHub Plugin
+
+The first step is to bootstrap a new plugin for the ToolJet marketplace. The plugin will authenticate users with a GitHub Personal Access Token and include fundamental operations such as fetching user details, repositories, issues, and pull requests.
+
+If you have completed the **[Setup](/docs/contributing-guide/marketplace/marketplace-setup)** guide, you can begin developing the plugin using the `tooljet` CLI. To initiate plugin development, enter the following command in the terminal:
+```bash
+# create a new plugin
+tooljet plugin create github
+```
+
+When prompted, enter the **plugin name** and select the **plugin type**, which is api in this case. Additionally, select **yes** when prompted to create a new plugin for the marketplace.
+
+If your plugin is hosted on GitHub, please provide the **repository URL** when prompted. Otherwise, leave it blank.
+
+When a plugin is created using the `ToolJet` CLI, an object is added to the **plugins.json** file in the **`ToolJet/server/src/assets/marketplace/`** directory. This object includes metadata about the plugin, such as its name, description, version, author, and other relevant details.
+
+The plugins.json file serves as a registry of all available plugins for use in ToolJet. When the ToolJet server starts up, it reads the plugins.json file and loads all plugins that are listed in it.
+
+:::info
+It is important to note that the plugins.json file should not be manually edited, as it is automatically generated by the `ToolJet CLI`. Making changes to this file can result in issues with the proper functioning of the plugins in the system.
+:::
+
+All marketplace plugins are stored in the **`/marketplace`** directory of the ToolJet repository. You can find the GitHub plugin **[here](https://github.com/ToolJet/ToolJet/tree/develop/marketplace/plugins/github)**.
+
+The structure of a typical ToolJet plugin directory appears as follows:
+```bash
+github/
+ package.json
+ lib/
+ icon.svg
+ index.ts
+ operations.json
+ manifest.json
+```
+
+In this structure, the file **manifest.json** contains information about the plugin's name, description, and other details. The file **operations.json** contains metadata about all the operations that the plugin supports. The main file, **index.ts**, creates a QueryService for the plugin, which handles queries, connection testing, caching, and more. The **icon.svg** file serves as the plugin's icon, while **package.json** is automatically generated by the CLI.
+
+:::info
+**Why do we need a manifest.json file or a operations.json file?**
+
+The manifest.json file is used by a React component to create a dynamic UI for connection forms. It defines the schema of an API or data source, including its name, type, and any exposed variables, as well as options for authentication and other customizable properties. The properties section specifies the required fields and their types for connecting to the API or data source. By reading the manifest.json file, the React component generates the necessary UI components based on the schema, such as text inputs, dropdowns, checkboxes, and other elements.
+
+On the other hand, the operations.json file contains a schema definition for a specific data source, like Github. It describes the available operations and their parameters that can be used to query the data source. A React component uses this schema to create queries in ToolJet applications, generating a UI that allows users to select the desired operation and provide the required parameters. The component uses the properties defined in the operations.json file to create various UI elements, such as dropdowns and input fields, and handle user interactions to create the final query. Once the user fills in the required parameters, the component uses them to generate a query that can be executed against the data source and return the results to the user.
+
+Overall, *manifest.json* and *operations.json* files are essential for creating dynamic UI components in ToolJet applications. They define the schema for data sources and available operations, which React components then use to generate user-friendly UI elements. By utilizing these files, ToolJet enables users to easily connect to various APIs and data sources, perform queries, and retrieve data in an intuitive and efficient manner.
+:::
+
+## Step 2: Defining the manifest.json file
+
+To construct the connection form, it's important to include the necessary options in the manifest.json file. Here's an example of how to do it:
+```json
+ "properties": {
+ "credentials": {
+ "label": "Authentication",
+ "key": "auth_type",
+ "type": "dropdown-component-flip",
+ "description": "A single select dropdown to choose credentials",
+ "list": [
+ {
+ "value": "personal_access_token",
+ "name": "Use Personal Access Token"
+ }
+ ]
+ },
+ "personal_access_token": {
+ "token": {
+ "label": "Token",
+ "key": "personal_token",
+ "type": "password",
+ "description": "Enter your personal access token",
+ "hint": "You can generate a personal access token from your Github account settings."
+ }
+ }
+ }
+```
+This manifest.json file includes information about authentication options, specifically a dropdown to choose a type of credentials and a field to enter a personal access token. The label, key, type, description, and hint properties are used to define the specific fields and their types required for connecting to the API or data source.
+
+In this particular code, there are two main properties defined: **`credentials`** and **`personal_access_token`**.
+
+The **`credentials`** property specifies the authentication method to be used. It contains several keys:
+- **`label`**: a user-friendly label for the authentication method, set to "Authentication"
+- **`key`**: a unique identifier for the authentication method, set to "auth_type"
+- **`type`**: the type of the authentication method, set to "dropdown-component-flip"
+- **`description`**: a description of the authentication method, set to "A single select dropdown to choose credentials"
+- **`list`**: an array of objects representing the different authentication methods available. In this case, there is only one method available: a personal access token. The `value` key in the object is set to "personal_access_token" and the `name` key is set to "Use Personal Access Token".
+
+The **`personal_access_token`** property specifies the details of the personal access token authentication method. It contains a `token` key, which specifies the actual personal access token to be used. The `token` key contains several keys:
+- **`label`**: a user-friendly label for the personal access token, set to "Token"
+- **`key`**: a unique identifier for the personal access token, set to "personal_token"
+- **`type`**: the type of the personal access token, set to "password"
+- **`description`**: a description of the personal access token, set to "Enter your personal access token"
+- **`hint`**: a hint for the personal access token, set to "You can generate a personal access token from your Github account settings."
+
+The available `type` options are:
+
+However, based on the code you provided, the available **`type`** options are:
+- **`password`**: used to input a secret value, such as a password or an access token.
+- **`dropdown-component-flip`**: used to create a dropdown menu that flips its position relative to the component that triggers it.
+- **`text`**: used to input a single line of text.
+- **`textarea`**: used to input multiple lines of text.
+- **`toggle`**: used to create a simple on/off switch.
+- **`react-component-headers`**: used to display headers for React components.
+- **`codehinter`**: is a specialized input field used for entering code and has additional functionality, such as resolving JavaScript code within double curly braces`{{}}`.
+
+:::tip
+The **manifest.json** file is utilized by the connection modal component, which appears to prompt users to enter their datasource credentials. Meanwhile, the **operations.json** file is used by the query manager when users generate a specific query for a connected datasource. **Both files utilize a similar schema**.
+:::
+
+## Step 3: Defining the operations.json file
+```json
+ "properties": {
+ "operation": {
+ "label": "Operation",
+ "key": "operation",
+ "type": "dropdown-component-flip",
+ "description": "Single select dropdown for operation",
+ "list": [
+ {
+ "value": "get_user_info",
+ "name": "Get user info"
+ },
+ {
+ "value": "get_repo",
+ "name": "Get repository"
+ },
+ {
+ "value": "get_repo_issues",
+ "name": "Get repository issues"
+ },
+ {
+ "value": "get_repo_pull_requests",
+ "name": "Get repository pull requests"
+ }
+ ]
+ },
+ "get_user_info": {
+ "username": {
+ "label": "Username",
+ "key": "username",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter username",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "Enter username"
+ }
+ },
+ "get_repo": {
+ "owner": {
+ "label": "Owner",
+ "key": "owner",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter owner name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "developer"
+ },
+ "repo": {
+ "label": "Repository",
+ "key": "repo",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter repository name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "tooljet"
+ }
+ },
+ "get_repo_issues": {
+ "owner": {
+ "label": "Owner",
+ "key": "owner",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter owner name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "developer"
+ },
+ "repo": {
+ "label": "Repository",
+ "key": "repo",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter repository name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "tooljet"
+ },
+ "state": {
+ "label": "State",
+ "key": "state",
+ "className": "codehinter-plugins col-4",
+ "type": "dropdown",
+ "description": "Single select dropdown for choosing state",
+ "list": [
+ {
+ "value": "open",
+ "name": "Open"
+ },
+ {
+ "value": "closed",
+ "name": "Closed"
+ },
+ {
+ "value": "all",
+ "name": "All"
+ }
+ ]
+ }
+ },
+ "get_repo_pull_requests": {
+ "owner": {
+ "label": "Owner",
+ "key": "owner",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter owner name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "developer"
+ },
+ "repo": {
+ "label": "Repository",
+ "key": "repo",
+ "type": "codehinter",
+ "lineNumbers": false,
+ "description": "Enter repository name",
+ "width": "320px",
+ "height": "36px",
+ "className": "codehinter-plugins",
+ "placeholder": "tooljet"
+ },
+ "state": {
+ "label": "State",
+ "key": "state",
+ "type": "dropdown",
+ "className": "codehinter-plugins col-4",
+ "description": "Single select dropdown for choosing state",
+ "list": [
+ {
+ "value": "open",
+ "name": "Open"
+ },
+ {
+ "value": "closed",
+ "name": "Closed"
+ },
+ {
+ "value": "all",
+ "name": "All"
+ }
+ ]
+ }
+ }
+ }
+```
+The operations.json file specifies the available operations that can be executed on the data source. It provides details about the operation type, required fields to execute the operation, and the data type of each field. The label, key, type, description, and hint properties are used to define the specific fields and their types required to establish a connection with the API or data source.
+
+## Step 4: Add the npm package of GitHub to the plugin dependencies
+
+- Change directory to the plugin directory where the npm package needs to be installed and then install the package
+ ```bash
+ # change directory to the plugin directory and install the npm package
+ npm i octokit --workspace=@tooljet-marketplace/github
+ ```
+
+ :::info
+ Steps to install npm package to a plugin
+
+ ```bash
+ npm i --workspace=
+ ```
+
+ The command `npm i --workspace=` is used to install a specific npm package into a particular workspace of a multi-package repository.
+
+ The *--workspace* flag is used to specify the workspace where the package should be installed. In this case, we are installing the package in the *@tooljet-marketplace/github* workspace.
+ :::
+
+## Step 5: Implement the query execution logic in index.ts
+
+In index.ts, the query execution logic needs to be implemented for the Github plugin's QueryService. The QueryService is responsible for handling the process of running queries and receives information about the data source, including credentials, configurations, and query parameters.
+
+For the Github data source, the sourceOptions will contain the necessary authentication credentials, like the personal access token, while the queryOptions will include the configurations and parameters specific to the query, like obtaining a list of repositories for a particular user.
+
+Using this information, the QueryService will create and execute API requests against the Github API. The resulting data will be returned to the caller for further processing as needed.
+
+Create a new file **query_operations.ts** in the **plugins/github/src** directory and add the following code to it.
+```typescript
+import { Octokit } from 'octokit'
+import { QueryOptions } from './types'
+
+
+export async function getUserInfo(octokit: Octokit, options: QueryOptions): Promise