BETA NOTICE: Widget Builder is currently in active beta. Features, APIs, and interface elements may evolve as we refine developer tooling.
Access is available on staging environments for testing, and select customers have approved early access in production.
For the latest updates or to request beta access, contact your Customer Success Manager (CSM) or Account Executive.
A Data Source is a saved, reusable connection to an external HTTP API. Once an administrator configures a Data Source, it becomes available in Widget Builder, where it can be used to pull live data into your Custom Widgets without hard-coding URLs, secrets, or authentication details into widget code.
You can think of a Data Source as a secure, named "doorway" to an API. You define the doorway once (base URL, headers, which paths and methods are allowed, and who is permitted to use it), and from then on your widgets simply reference it by name.
NOTE: Your community includes a vanillaAPIv2 Data Source. It calls your own community's API directly and supports all HTTP methods. Use it to pull in discussions, users, categories, and anything else from the Vanilla API without configuring a thing.
Data Sources extend Widget Builder by giving developers a structured way to configure reusable API connections, preload data into widgets, and make secure authenticated requests through the Data Sources interface.
WARNING: Widget Builder is a powerful, code-first customization interface designed for developers who are skilled in JavaScript, CSS, React, and other design principles. With this in mind, make sure you possess the proper coding expertise and knowledge before using this tool—or work with someone who does. Otherwise, you may damage the integrity and functionality of your Higher Logic Vanilla (Vanilla) community.
For guidance on safe customization, review our Widget Builder Best Practices.
Supported authentication methods
A Data Source can use one of three authentication models:
- None, for endpoints that do not require authentication.
- Header-based authentication, for APIs that expect one or more headers such as API keys or bearer values.
This approach keeps connection details centralized and reusable instead of hardcoding them into widget code.
Why use Data Sources?
- Security: API keys and secret headers are stored encrypted on the server and are never exposed to the browser. Requests are proxied through your community, so credentials stay server-side.
- Reuse: Configure an API connection once and use it across as many Custom Widgets as you like.
- Performance: Data can be preloaded on the server while the page is being built, so widgets render with data already present instead of showing a loading spinner and fetching after the page loads.
- Developer experience: Configured Data Sources appear in the Widget Builder's code editor with full IntelliSense (autocomplete, type hints, and inline documentation).
Access Data Sources
- Access the Dashboard.
- Navigate to Settings > API Integrations > Data Sources.
Create a Data Source
NOTE: Creating a Data Source requires the Garden > Settings > Manage permission.
On the Data Sources page, click Add Data Source at the top right to open the create page and begin the process.
Configure your Data Source
A Data Source's configuration fields are organized into three sections. Let's walk through each set of options.
Data Source Details
- Name: A friendly, human-readable name (for example,
Test API). - Data Source ID: A unique identifier used to reference this Data Source in code.
IMPORTANT: This cannot be changed after creating the Data Source.
- Description: Explains the Data Source's purpose and what it provides. This description is shown to widget builders when they select it.
- Documentation URL: A link to the API's docs, surfaced in the Widget Builder for developers.
API Details
- Base URL: The root URL of the API (for example,
https://api.example.com). All endpoint paths are appended to this. - Request Headers: Non-sensitive headers as JSON.
- Secret Headers: Sensitive headers (for example,
{"X-Api-Key": "supersecret123"}).
IMPORTANT: Once saved, the secret headers value cannot be viewed again. If you need to edit it, you will need to provide the entire value once again.
Secret headers are encrypted and write-only; leaving the field empty when editing means "no change". Your saved secret is preserved. You only need to re-enter it if you want to replace it.
Access Control
- Allowed Paths: One path per line. Supports wildcards (e.g.
/users*). Leave empty to allow all paths. Requests to paths that aren't allowed are rejected. - Allowed Methods: The HTTP methods (
GET, POST, PUT, DELETE, PATCH) this Data Source permits. Only these will be callable from widgets. - Access Control Type: This option controls whether there are any restrictions on who can view the widgets that use this Data Source.
- If you want widgets associated to this Data Source available to all of your community users, leave it set to No Restrictions.
- If you want to restrict access to only community users belonging to specific Role(s) and/ Rank(s), select Restricted to Specific Roles/Ranks and select them in the displayed dropdowns.
Test the connection (optional but recommended)
Before creating your Data Source, we recommend testing it. To do so, click Test Connection to open the test dialog.
This lets you verify the API responds correctly using your configured base URL and headers.
Using a Data Source in Widget Builder
Once a Data Source is created, you use it in Widget Builder when authoring a Custom Widget.
NOTE: Data Sources can be restricted to specific Roles and/or Ranks, so usage of them may be limited to certain users in your community.
There are two complementary ways to consume data:
- Preloaded Data: Configured visually on the Preload Data tab. Data is fetched on the server and handed to your widget as props.
- Runtime calls in code: Written in TypeScript inside index.tsx. Data is fetched in the browser when the widget runs.
TIP: Use Preloaded Data for content that should be visible immediately on page load (it's faster and SEO-friendly).
Use runtime calls for data that depends on user actions or that you want to refresh after the page has loaded.
They work well together: preload the initial view, then use runtime calls for "load more."
Preloaded Data (preloaded calls)
A preloaded call is a request that runs on the server while the page is being assembled. By the time the page reaches the browser, the response is already embedded in the widget's props. There's no second network round-trip and no loading state.
Configuring a preloaded call
- In Widget Builder, click the Preload Data tab. If nothing is configured yet, you'll see an empty state.
- Click Add Preload Data to open the configuration dialog.
- In the Data Source dropdown, pick a source. A small info panel shows the source's description, auth type, allowed methods, and whether it's access-restricted.
- Configure the query:
- Method: Limited to the source's allowed methods
- Endpoint Path: Appended to the source's base URL (for example
/messages). - Prop Field Name: The prop your result is stored under. A field name of
testData exposes the result as props.testData in code. These prop names should be unique - Query Parameters: Key/value pairs appended to the URL. Values can be static text or references. Refer to the Parameter references section below to learn more.
- Array Handling: Controls how array values are serialized:
- Brackets:
?ids[]=1&ids[]=2 - Indices:
?ids[0]=1&ids[1]=2 - Comma:
?ids=1,2 - Repeat:
?ids=1&ids=2
- Body Parameters: Appear for methods that send a body (POST, PUT, PATCH).
- (optional) Click + Add Query if you want to add multiple queries to one Data Source.
- When finished, click Save.
TIP: Limit the number of preloaded queries. Be mindful of the number of queries a single widget will make. Calling too many or slow responding APIs could slow down your page or render with partial data. Always code defensively to provide users a graceful fallback or informative error state.
Parameter references
When building query or body parameters for a preloaded call, values don't have to be static. They can reference live context, resolved on the server before the request runs:
Reference type | Example | Meaning |
|---|
Static value | my value
| A literal value you type |
Prop reference | props.discussion.discussionID
| A value taken from the widget's props |
Current user | currentUser.userID
| A field from the signed-in user. (e.g. currentUser.userID, currentUser.name, currentUser.roleIDs, currentUser.rankID) |
This lets you build personalized requests, such as fetching data scoped to the current user's ID without writing any code.
Reading preloaded data in your widget
Preloaded results arrive as ordinary props. If you set the prop field name to testData, read it directly:
export default function CustomFragment(props: Custom.props) {
// `props.testData` is already populated by the preloaded call
const items = props.testData?.items ?? [];
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Runtime calls in Typescript
When you need to fetch data, call the Data Source directly from your TypeScript code. Vanilla automatically injects a DataSources object that exposes every configured source.
TIP: Widget Builder's code editor is aware of your created Data Sources. As you type, you'll get autocomplete and type checking. Sources are suggested even if you haven't configured a preloaded call for them, so you can write runtime calls against any source freely.
The import is added automatically when you configure your first preloaded call, but you can also add it yourself:
import DataSources, { type IDataSourceResponse, type IDataSourceConfig } from "@vanilla/injectables/DataSources";
Each Data Source is available as DataSources.<dataSourceID>, with one method per allowed HTTP verb plus a canUse() permission check:
const response = await DataSources.myTestApi.get("/messages", {
params: { read: true },
});
console.log(response.statusCode); // e.g. 200
console.log(response.body); // parsed JSON response
Gating on access with the "canUse()" method
DataSources.<id>.canUse() returns a boolean indicating whether the current user satisfies the source's role/rank access control. Use it to hide UI or skip requests for users who aren't permitted:
import DataSources from "@vanilla/injectables/DataSources";
import Utils from "@vanilla/injectables/Utils";
export default function CustomFragment(props: Custom.props) {
const canUse = DataSources.myTestApi.canUse();
const { data, isLoading } = Utils.useQuery({
queryKey: ["myTestApi", "get", { limit: 10 }],
// Only run the request for users who
// satisfy the source's access control
enabled: canUse,
queryFn: async () => {
const res = await DataSources.myTestApi.get("/get", {
params: { limit: 10 }
});
return res.body;
},
});
if (!canUse) {
return null;
}
return (
<>
{isLoading ? <span>Loading…</span> : <pre>{JSON.stringify(data, null, 2)}</pre>}
</>
);
}