Load uservoice API on Click
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
If you only show a support widget when the user asks for help, loading the UserVoice script lazily is usually better than embedding it at page load. It reduces initial network work, delays third-party code until the user explicitly requests it, and makes failures easier to isolate.
The important implementation detail is that the click handler should not inject the script over and over. Instead, create a one-time loader, wait for the script to finish, and only then call the widget API.
Load the Script Only Once
The safest pattern is to wrap the script injection in a promise and cache that promise for future clicks. That gives you one loading path no matter how many times the button is pressed.
This example is fully runnable as an HTML page. To make the actual UserVoice widget appear, replace YOUR_WIDGET_KEY with your real widget key and confirm that the push command matches the widget version your account uses.
Why the Promise Pattern Matters
Without a cached promise, repeated clicks can create multiple script tags and race each other. That leads to inconsistent behavior:
- the widget may initialize twice
- later clicks may run before the first load completed
- error handling becomes scattered across several branches
With a single loadUserVoice function, every click shares the same loading state. The first click starts the download. Later clicks reuse the same promise and wait for the same result.
That is especially useful on slow connections, where a user might click again because nothing appeared immediately.
Keep the Widget Call Separate From the Loader
Notice that loadUserVoice only loads the script and returns the API object. The UI action, which is opening the widget, happens in the click handler after the promise resolves.
That separation keeps responsibilities clear:
- the loader manages script lifecycle
- the click handler manages user interaction
- error messages stay close to the UI that triggered them
If your UserVoice setup uses a different command than ["showLightbox", "classic_widget"], change only the click-handler block. The loader code stays the same.
Handle Failure and Policy Constraints
Third-party scripts can fail for reasons that have nothing to do with your code:
- the widget key is wrong
- a content security policy blocks the script host
- an ad blocker or privacy extension blocks the request
- the network is offline
That is why the catch block is important. A failed widget load should not break the rest of the page. Show a fallback message or a regular contact link so support remains reachable.
You should also think about consent and privacy requirements. Loading UserVoice only after a click may align better with your policy than eagerly loading the widget for every visitor.
Common Pitfalls
- Injecting the script on every click instead of caching the first load attempt.
- Calling
UserVoice.pushbefore the script has finished loading. - Hard-coding a widget command that does not match the UserVoice version in use.
- Ignoring content security policy or browser-extension blocking, which can make the script appear randomly broken.
- Forgetting a fallback path. Support should still be reachable even if the third-party widget fails.
Summary
- Lazy loading UserVoice on click improves startup performance and delays third-party code until the user asks for it.
- Wrap the script injection in a cached promise so the widget loads only once.
- Wait for the promise to resolve before calling the UserVoice API.
- Keep the loader generic and the widget-open command in the click handler.
- Always handle load failures and provide a fallback support path.
Related reading
- Local Storage vs Cookies
- Logging request/response messages when using HttpClient
- Logs complaining extensions/v1beta1 Ingress is deprecated
- Looking for help in making my socket messenger send instantaneously in Python
- loading js files and other dependent js files asynchronously
- Locale based sort in Javascript, sort accented letters and other variants in a predefined way
- Looping and asynchronous connections in objective-c
- Main differences between SOAP and RESTful web services in Java

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.