Welcome to the Slack Form SDK, a tool designed to simplify the creation of interactive forms for Slack apps. This SDK empowers developers to effortlessly build forms, enhancing productivity within Slack teams.
Developing forms for Slack apps often involves navigating complex configurations and repetitive tasks. This SDK aims to alleviate these hurdles by abstracting away the complexities, allowing developers to focus on core functionality. Hence writing 3x Less code.
- Effortlessly create Slack forms with minimal code.
- Supports all basic form elements including text, text inputs, option selectors, checkboxes, radio buttons, user(s) selection, and channel(s) selection.
- Customizable options for each form element.
- Render Slack Modals with ease using the
renderModal
method. - Set initial form values using the
setFormValues
method. - Retrieve all form values at once with the
getFormValues
method. - Easily handle custom errors with the
setErrors
method. - Dynamically add or remove blocks based on conditions using
addBlock(s)
andremoveBlocks
methods.
-
Text
-
JSON Object Representation:
{ "key": "order-feedback-label", "type": "text", "disabled": false, "textType": "context | header | md", // default is md "value": "Feedback form about your recent purchase." }
-
-
Select
-
JSON Object Representation:
{ "key": "satisfaction-level", "type": "select", "label": "Satisfaction level rating", "placeholder": "Select your satisfaction level", "watch": false, // If true it dispathes the slack action, defaults to false. "required": true, "disabled": false, "multiselect": false }
-
-
User Select
-
JSON Object Representation:
{ "key": "feedback-about", "type": "user-select", "label": "Sales Representative(s)", "placeholder": "Choose recipient(s)", "watch": false, "required": true, "disabled": false, "multiselect": true }
-
-
Channel Select
-
JSON Object Representation:
{ "key": "channel", "type": "channel-select", "label": "Select Channel(s)", "placeholder": "Choose channel(s)", "watch": false, "required": false, "disabled": false, "multiselect": true }
-
-
Text Input:
-
JSON Object Representation:
{ "key": "feedback-text", "type": "text-input", "label": "Your feedback", "placeholder": "Type your feedback here", "watch": false, "hint": "Public", "required": true, "disabled": false, "multiline": true }
-
-
Radio
-
JSON Object Representation:
{ "key": "product-quality", "type": "radio", "label": "How was the Product quality ?", "watch": false, "required": true, "disabled": false }
-
-
Checkbox
-
JSON Object Representation:
{ "key": "issues-faced", "type": "checkbox", "label": "Select any Issue(s) faced by you", "watch": false, "required": true, "disabled": false }
-
-
Date Picker
-
JSON Object Representation:
{ "key": "date", "type": "date-picker", "label": "Date of Purchase", "watch": false, "required": true, "disabled": false }
-
-
Time Picker:
-
JSON Object Representation:
{ "key": "time", "type": "time-picker", "label": "Time of Purchase", "watch": false, "required": false, "disabled": false }
-
-
Date Time Picker
-
JSON Object Representation:
{ "key": "dateTime", "type": "date-time-picker", "label": "Select Date and Time", "watch": false, "required": true, "disabled": false }
-
-
Button
-
JSON Object Representation:
{ "key": "preview", "type": "button", "text": "Preview the form here", "label": "View", "disabled": false }
Generate forms by defining an array of form elements and passing it as the form
object.
// Main array for your form.
const feedbackFormElements = [
{
key: "order-feedback-label",
type: "text",
disabled: false,
value: "Feedback form about your recent purchase.",
},
{
key: "satisfaction-level",
type: "select",
label: "Satisfaction level rating",
placeholder: "Select your satisfaction level",
required: true,
disabled: false,
multiselect: false,
},
{
key: "feedback-about",
type: "user-select",
label: "Sales Representative(s)",
placeholder: "Choose recipient(s)",
required: true,
disabled: false,
multiselect: true,
},
{
key: "selected-channel",
type: "channel-select",
label: "Select Channel(s)",
placeholder: "Choose channel(s)",
required: false,
disabled: false,
multiselect: true,
},
{
key: "feedback-text",
type: "text-input",
label: "Your feedback",
placeholder: "Type your feedback here",
hint: "Public",
required: true,
disabled: false,
multiline: true,
},
{
key: "product-quality",
type: "radio",
label: "How was the Product quality ?",
required: true,
disabled: false,
},
{
key: "issues-faced",
type: "checkbox",
label: "Select any Issue(s) faced by you",
required: false,
disabled: false,
},
{
key: "date",
type: "date-picker",
label: "Date of Purchase",
required: true,
disabled: false,
},
{
key: "time",
type: "time-picker",
label: "Time of Purchase",
required: false,
disabled: false,
},
{
key: "preview",
type: "button",
text: "Preview the form here",
label: "View",
disabled: false,
},
];
// Actual form object
const form = {
id: "feedback-form", // must be unique.
blocks: feedbackFormElements,
optionValues: {
// Option values are required for type " "select" | "radio" | "checkbox"
// Here "satisfaction-level" is the key defined in our feedbackForm array which is of type "select"
"satisfaction-level": [
{ text: "Very Satisfied", value: "very-satisfied", description: "Top Rank" },
{ text: "Satisfied", value: "satisfied" },
{ text: "Neutral", value: "neutral" },
{ text: "Dissatisfied", value: "dissatisfied" },
{ text: "Very Dissatisfied", value: "very-dissatisfied", description: "Low Rank" },
],
// Here "product-quality" is the key defined in our feedbackForm array which is of type "radio"
"product-quality": [
{ text: "Excellent", value: "excellent", description: "Top Rank" },
{ text: "Good", value: "good" },
{ text: "Average", value: "average" },
{ text: "Poor", value: "poor", description: "Low Rank" },
],
// Here "issues-faced" is the key defined in our feedbackForm array which is of type "checkbox"
"issues-faced": [
{ text: "Late Delivery", value: "late-delivery" },
{ text: "Wrong Product", value: "wrong-product" },
{ text: "Damaged Product", value: "damaged-product" },
{ text: "Billing Error", value: "billing-error" },
{ text: "Other", value: "other", description: "Please specify an issue" },
],
},
};
const { SlackFormManager } = require("slack-form");
const {
renderModal,
renderForm,
getFormValues,
setFormValues,
setErrors,
addBlock,
addBlocks,
removeBlocks,
} = SlackFormManager.create(form, stateValues);
SlackFormManager.create
accepts two arguments
- Actual form object which we create in the previous section Form Generation
- stateValue (payload.view.state.values) is the form data Slack sent to the server when any dispatch action or form is submitted.
renderModal
renderForm
getFormValues
setFormValues
setErrors
addBlock
addBlocks
removeBlocks
When you want to render a form in Slack's modal format,
In your event action handler use this method renderModal
const { renderModal } = SlackFormManager.create(form, stateValues);
const modal = renderModal();
views.open(modal);
console.log(JSON.stringify(modal));
renderForm
is same as renderModal
. The only difference is it returns the Slack blocks instead of the entire Slack modal.
const { renderForm } = SlackFormManager.create(form, stateValues);
const slackBlocks = renderForm();
Getting form values is as easy as calling this method, You'll directly get the form submitted values as key-value pairs with the "key" you defined in your actual form blocks array.
const { getFormValues } = SlackFormManager.create(form, stateValues);
const formValues = getFormValues();
console.log(formValues);
// output:-
{
'satisfaction-level': 'very-satisfied',
'feedback-about': [ 'U06NRHQ018B' ],
'feedback-text': 'Hey nice delivery... Very much on time.',
'product-quality': 'excellent',
'issues-faced': [ 'other' ],
date: '2024-03-24',
time: '07:00'
}
You can set your initial form values before you render your modal with this method, Here keys will be the "key" you defined in your actual form blocks array.
const { setFormValues, renderModal } = SlackFormManager.create(
form,
stateValues
);
setFormValues({
"feedback-text": "Poor product deliveed",
"product-quality": "bad",
"issues-faced": ["other"],
});
const modal = renderModal();
views.open(modal); // Will render the modal with initial values provided.
Slack has error handling, but it's not that customizable.With setErrors, you can define your custom errors as Markdown texts at each form element. Here keys will be the "key" you defined in your actual form blocks array.
const { setErrors, renderModal } = SlackFormManager.create(form, stateValues);
setErrors({
date: "⚠️ Date cannot be in the past",
time: "⚠️ Time cannot be in the past",
"feedback-text": "📝 Feedback is required",
});
const modal = renderModal();
views.update(modal);
Based on any conditions you can add form element dynamically anywhere you want.
Here's the thing you should know about addBlock
Add block accepts 2 arguments
- The actual element object you want to add dynamically in your form
- Location, Where you want to actually add in the form ?
- If you don't pass anything it'll be by default added at the end of the form
- If you pass
start
it'll be added at the start of the form- If you pass like
after::"key"
orbefore::"key"
it'll add before or after the form element whom key you've provided.
const { renderModal, getFormValues, addBlock } = SlackFormManager.create(
form,
stateValues
);
const formValues = getFormValues();
if (formValues["issues-faced"].includes("other")) {
const otherIssueTextBlock = {
key: "other-issue",
type: "text-input",
label: "Your feedback",
placeholder: "Describe the issue you faced",
required: true,
multiline: true,
};
addBlock(otherIssueTextBlock, `after::issues-faced`);
}
const modal = renderModal();
views.update(modal); // now you can re-render your modal, you'll notice the new block dynamically got added
addBlocks
is same as addBlock
but unlike adding a single form element,
it accepts an array of form elements to add them in bulk.
Sill what will be location of form elements to be added ?
- Each form element will have one optional key
"location"
with values likeafter::"key"
,before::"key"
andstart
- If you don't key
"location"
pass anything it'll be by default added at the end of the form.
const { renderModal, getFormValues, addBlocks } = SlackFormManager.create(
form,
stateValues
);
const formValues = getFormValues();
if (formValues["issues-faced"].includes("other")) {
const blocksToBeAdded = [
{
key: "product-quality-feedback",
type: "text-input",
label: "Your feedback",
placeholder: "Any comments on product Quality?",
required: false,
multiline: true,
location: `after::product-quality`, // This block be added after the form element with the key "product-quality"
},
{
key: "other-issue-feedback",
type: "text-input",
label: "Your feedback",
placeholder: "Describe the issue you faced",
required: true,
multiline: true,
location: `after::issues-faced`, // This block be added after the form element with the key "issues-faced"
},
];
addBlocks(blocksToBeAdded);
}
const modal = renderModal();
views.update(modal); // now you can re-render your modal, you'll notice the new block(s) dynamically got added
removeBlocks
let's you remove the form element(s).
Accepts array of form element "keys"
const { renderModal, removeBlocks } = SlackFormManager.create(
form,
stateValues
);
removeBlocks(["product-quality", "issues-faced"]);
const modal = renderModal();
views.update(modal); // now you can re-render your modal, you'll notice the new block(s) dynamically got removed
With the usage of this form library, All the blockId and ActionId are auto generated and handled by the library itself, Here's what generated block Id and actionId looks like:-
Generated Block Id => {formId}::{key}::block
Generated Action Id => {formId}::{key}::action
For instance consider below form element.
const formId = "give-feedback-form";
...
{
key: "satisfaction-level",
type: "select",
label: "Satisfaction level rating",
placeholder: "Select your satisfaction level",
required: true,
multiselect: false,
watch: false
}
...
Since no actionId
or blockId
is passed in above example,
The auto generated ActionId and BlockId will look like:-
- Generated Block Id =>
{formId}::{key}::block
- Generated Action Id =>
{formId}::{key}::action
actionId: "give-feedback-form::satisfaction-level::action"
blockId: "give-feedback-form::satisfaction-level::block"
However there may be sistuation when you want to override this default behaviour and assign you own custom actionId and blockId, You can easly to that passing your own actionId and blockId.
...
{
key: "satisfaction-level",
type: "select",
label: "Satisfaction level rating",
placeholder: "Select your satisfaction level",
required: true,
multiselect: false,
watch: false,
actionId: "my-custom-action-id", // custom action_id
blockId: "my-custom-block-id" // custom block_id
}
...