Appearance
Are you an LLM? You can read better optimized documentation at /docs/custom-widgets/v2/build-first-widget.md for this page in Markdown format
Your First Widget
In this tutorial, you will create a custom widget from scratch, configure it with user-editable settings, and publish it to your community. By the end, you will have a working widget in the No-Code Builder that responds to live configuration changes.
Before You Start
- You have a GitHub account.
- You have access to Sources in your platform (Integrations → Developer Studio → Sources).
- You have a connected GitHub organization — see Connect Your GitHub Account if you haven't connected one yet.
- You have a repository from the template, enabled in Sources with
mainas the watched branch — see Repository Layout and Repository & Branch Settings.
Step 1: Create the Widget File
In your repository, create the directory widgets/greeting/ and inside it create index.html with this content:
html
<style>
.greeting-card {
font-family: var(--config--main-font-family, sans-serif);
padding: 24px;
border-radius: 8px;
background: var(--config--main-color-background, #ffffff);
border: 1px solid var(--config--main-color-border, #e5e7eb);
text-align: center;
}
.greeting-card h2 {
color: var(--config--main-color-brand, #2563eb);
margin: 0 0 8px;
}
.greeting-card p {
color: var(--config--main-color-text, #374151);
margin: 0;
}
</style>
<div class="greeting-card">
<h2 class="title">Hello, Community!</h2>
<p class="subtitle">This is your first custom widget.</p>
</div>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Notice that the styles use var(--config--...) CSS custom properties. These are design tokens — the platform injects your community's branding values for colors and fonts automatically. The second argument to each var() is a fallback so the widget looks reasonable outside a community context.
You now have the widget's HTML file. It is a fragment — no <html>, <head>, or <body> tags.
Step 2: Register the Widget
Open extensions_registry.json at the root of your repository and add this entry to the widgets array:
json
{
"widgets": [
{
"title": "Greeting Card",
"type": "my_first_widget_greeting",
"description": "A simple greeting card widget",
"category": "custom",
"source": {
"path": "widgets/greeting",
"entry": "index.html"
}
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
The type field is the widget's unique identifier across your entire community. The value my_first_widget_greeting is namespaced to avoid conflicts with other widgets — keep this pattern when you build real widgets.
The source block tells the platform where your widget files live in the repository. path is the directory; entry is the HTML file inside it.
These are the required fields. There are more options you can use to customize the widget — see Widget Definition Reference.
Step 3: Push and Publish
Commit and push your changes:
bash
git add extensions_registry.json widgets/greeting/index.html
git commit -m "Add greeting widget"
git push1
2
3
2
3
Go back to Integrations → Developer Studio → Sources and watch the build status for your repository. After a few seconds it will change to Completed.
Your widget now appears in the No-Code Builder's widget library.
If the status shows Failed, open the build details to see the error message. The most common cause at this stage is a JSON syntax error in extensions_registry.json — paste the file into jsonlint.com to check it.
Step 4: Add It to a Page
- Open the No-Code Builder on any page in your community.
- Click the widget library icon or the Add widget button.
- Find Greeting Card in the list.
- Drag it onto the page or click Add.
You should see your greeting card rendered on the page. The heading color matches your community's brand color, and the background and border reflect your community's design tokens — that is the var(--config--...) properties at work.
If you do not see the widget in the library, verify that the build status is Completed.
Step 5: Add Configuration
The widget currently shows fixed text. In this step you will add three configuration fields so an admin can change the heading text, the subtext, and the card's background color.
Update the widget entry in extensions_registry.json so it looks like this:
json
{
"widgets": [
{
"title": "Greeting Card",
"type": "my_first_widget_greeting",
"description": "A simple greeting card widget",
"category": "custom",
"source": {
"path": "widgets/greeting",
"entry": "index.html"
},
"configuration": {
"properties": [
{
"name": "greeting_title",
"type": "text",
"label": "Heading",
"description": "The main heading shown on the card.",
"defaultValue": "Hello, Community!",
"rules": { "required": true, "maxLength": 80 }
},
{
"name": "greeting_subtitle",
"type": "text",
"label": "Subtext",
"description": "The smaller text below the heading.",
"defaultValue": "This is your first custom widget.",
"rules": { "maxLength": 160 }
},
{
"name": "card_background",
"type": "color",
"label": "Card Background",
"description": "Override the card background color.",
"defaultValue": "#ffffff"
}
]
},
"defaultConfig": {
"greeting_title": "Hello, Community!",
"greeting_subtitle": "This is your first custom widget.",
"card_background": "#ffffff"
}
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
The configuration.properties array defines the three fields that appear in the No-Code Builder form, and defaultConfig sets their starting values.
Your registry now defines three configurable properties.
Step 6: Read Configuration in Your Widget
The configuration values are available at runtime via sdk.getProps(). You need two changes: a JavaScript module file and a script tag in your HTML.
Create widgets/greeting/app.js:
javascript
export async function init(sdk) {
await sdk.whenReady()
const title = sdk.$('.title')
const subtitle = sdk.$('.subtitle')
const card = sdk.$('.greeting-card')
function applyProps(props) {
if (title) title.textContent = props.greeting_title || 'Hello, Community!'
if (subtitle) subtitle.textContent = props.greeting_subtitle || 'This is your first custom widget.'
if (card) card.style.background = props.card_background || ''
}
applyProps(sdk.getProps())
sdk.on('propsChanged', (newProps) => {
applyProps(newProps)
})
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The export keyword makes this an ES module — the platform discovers the init function and calls it with an SDK instance. The SDK provides $() as a shorthand for querying elements inside the widget's Shadow DOM.
Now update widgets/greeting/index.html to load the module:
html
<style>
.greeting-card {
font-family: var(--config--main-font-family, sans-serif);
padding: 24px;
border-radius: 8px;
background: var(--config--main-color-background, #ffffff);
border: 1px solid var(--config--main-color-border, #e5e7eb);
text-align: center;
}
.greeting-card h2 {
color: var(--config--main-color-brand, #2563eb);
margin: 0 0 8px;
}
.greeting-card p {
color: var(--config--main-color-text, #374151);
margin: 0;
}
</style>
<div class="greeting-card">
<h2 class="title">Hello, Community!</h2>
<p class="subtitle">This is your first custom widget.</p>
</div>
<script type="module" src="app.js"></script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
The init(sdk) function is the entry point the platform calls after loading your widget. await sdk.whenReady() waits until the SDK is fully initialized. sdk.$() queries elements inside your widget's Shadow DOM — it is scoped to your widget only.
sdk.getProps() returns the current configuration values. sdk.on('propsChanged', ...) fires every time an admin updates the configuration in the No-Code Builder, letting your widget update without a page reload.
Step 7: Publish and Test Configuration
Commit and push the updated files:
bash
git add extensions_registry.json widgets/greeting/index.html
git commit -m "Add configurable props to greeting widget"
git push1
2
3
2
3
Wait for the build status to show Completed in Sources.
Open the No-Code Builder, find your Greeting Card widget on the page, and click the configure icon (or right-click → Configure). You should see a form with three fields: Heading, Subtext, and Card Background.
Change the heading text. The widget updates in real time as you type. Change the background color — the card background changes immediately. This is the propsChanged event in action.
If the configuration panel does not appear, verify the build completed successfully after your last push.
What You Built
You created a widget file, registered it in the platform, pushed it to production, and added it to a page. You then defined three configuration fields and connected them to the widget's runtime using the init(sdk) contract. The widget now reads its initial configuration and responds to live updates from the No-Code Builder.
What's Next
- Widget Runtime — understand how widgets run, the Shadow DOM model, and the full SDK lifecycle
- Widget Configuration — all field types and validation rules for configuration properties
- Widget SDK Reference — every method and event on the SDK instance
- Using React — build widgets with React instead of vanilla JavaScript
- Rendering & DOM — Shadow DOM patterns and element querying in depth

