As a front-end developer, one of the common challenges you'll encounter when working with embeddable scripts or static widgets is how to inject dynamic configuration into a static JavaScript file.
Since static files can't fetch data from a backend or receive props like React components, they must rely on what's available in the DOM.
One elegant and widely supported solution is to use HTML data-* attributes.
In this article, I'll walk through how this pattern works, when to use it, and why it's a reliable choice for flexible, embeddable scripts.
๐ฏ The Problem
Let's say you're distributing a static script โ for example, a survey button, a help widget, or a third-party plugin. You want other developers (or non-dev users) to embed your script and be able to customize things like:
- Color scheme
- Position (left/right/bottom)
- Icon
But your JavaScript file is hosted on a CDN and cannot be dynamically modified.
So, how do you make it configurable?
โ The data-* Attribute Pattern
HTML data-* attributes let you attach custom metadata to elements. These values are accessible via JavaScript using the .dataset
API.
Here's how it works:
<div
id="my-widget"
data-theme="dark"
data-position="right"
data-lang="en"
></div>
<script src="https://cdn.example.com/widget.js"></script>
Inside widget.js
:
const el = document.getElementById("my-widget");
const config = {
theme: el.dataset.theme || "light",
position: el.dataset.position || "left",
lang: el.dataset.lang || "en"
};
// Use the config to customize your widget
initializeWidget(config);
This pattern is clean, readable, and doesn't require any global variables or external communication.
๐งฐ Why Use This Pattern?
โ Pros
- Fully decoupled: Your static JS doesn't rely on server-side rendering or build-time variables.
- Declarative: Easy for developers and non-developers to understand.
- Multiple instances: You can use this pattern for multiple widgets on the same page, each with its own configuration.
- Safe & flexible: Works across browsers, respects caching, and doesn't interfere with global scope.
โ Cons
- Data is limited to string values โ you'll need to manually parse JSON or booleans if needed.
- Relies on correct HTML structure (e.g., an element with the right id or class).
- If not documented properly, consumers may miss available options.
โ๏ธ Best Practices
-
Use clear naming: Stick to data- attributes like
data-color
,data-user-id
, etc. - Set safe defaults: In your JS, always provide fallback values.
- Validate input values (e.g., allowed themes or positions).
- Avoid sensitive data: Don't pass secrets, tokens, or anything private via data-*.
๐ค But Waitโฆ This Isn't the Only Way
While data-* attributes are great, they're definitely not the only option. Depending on your use case, you might consider one of these too:
1. Global Variables
<script>
window.myWidgetConfig = {
theme: 'dark',
position: 'right',
lang: 'fa'
}
</script>
<script src="https://cdn.example.com/widget.js"></script>
This works well for more structured data, but it pollutes the global namespace โ which can be risky in large apps or when multiple scripts are involved.
2. Query Parameters in Script URL
<script src="https://cdn.example.com/widget.js?theme=dark&lang=fa"></script>
You can then parse these inside the script using document.currentScript.src
. This is quick and easy for small config values, but can get messy fast if your configuration grows.
3. JSON Inside <script type="application/json">
Another trick is embedding configuration data as JSON in a special script tag:
<script type="application/json" id="my-widget-config">
{
"theme": "dark",
"lang": "fa"
}
</script>
Then in your JS:
const raw = document.getElementById("my-widget-config").textContent;
const config = JSON.parse(raw);
A bit more setup, but great for complex configs.
๐ Conclusion
If you're building embeddable JavaScript for third-party use, data-* attributes offer a simple, scalable, and standards-compliant way to receive configuration from the host HTML.
This pattern strikes the right balance between flexibility and safety โ especially in environments where your script is embedded across many contexts and domains.
Next time you're working on a static JS file that needs to behave dynamically, consider this technique. It's declarative, reliable, and works in every browser that matters.
๐ฌ Got thoughts, questions, or your own favorite tricks?
Let's continue the conversation in the comments.
- Have you implemented something like this before? Which method did you go with, and why?
- Have you tried more advanced techniques like Web Components or JSON-LD-based configurations?
I'd love to hear your take. Let's share ideas โ there's always something new to learn ๐ฑ
๐ If you found this useful, feel free to share it with your team or give it a clap!
Top comments (0)