@widgetic/ui-sdk) puts Widgetic’s ready-made pieces in your own web page with a few lines of code. Your customers use them without leaving your product and without a Widgetic account.
Preview. The pieces on this page are new. They are built and tested, and you can try them now, but small details can still change while this label is on. Tell us what you find at support@widgetic.com.
The pieces
The File Browser is covered on this page, and so is Workflows. The composition editor and the widget creator have their own options and events, in Canvas Embedding.
Four things hold for all of them:
- They work only for a partner in good standing, with storage. A session is made only while your account is active and your own storage works (see Every session needs your own storage). While your account is paused, the pieces already on your customers’ pages are refused too (see Account Status).
- No API key in the browser. Your server keeps the key and makes a short-lived editor session for the customer who is on the page. The page only ever holds that session’s token, and it can reach only that customer’s content. Workflows can also be shown to a signed-in person of your own team, with that person’s token: it never holds a key either.
- Your page stays yours. A piece is drawn inside a shadow root: your CSS cannot restyle it and its CSS cannot touch your page. The SDK writes nothing to
<html>or<body>, sends nopostMessageand never clears your container. - Small. The SDK is a loader of about 30 KB. A piece is downloaded from Widgetic’s CDN when you mount it, and only then.
How it works
1
Your server makes an editor session
When the page opens, your server asks Widgetic for a session for the signed-in customer (
POST /v1/managed-users/{id}/editor-sessions, with your API key) and returns the token. See Editor Sessions.2
Your page gives the SDK a getToken function
getToken fetches that token from your server. The SDK calls it when the piece starts and again shortly before the token runs out.3
The SDK mounts the piece
It downloads the piece, draws it in your container, and the piece talks to the Widgetic API with the token and nothing else.
Install
@latest. Replace <version> with it; npm view @widgetic/ui-sdk version prints the newest one:
Mount a piece
Give the container a height: the piece fills it./widgetic-session is the endpoint of yours that makes the session (see A server endpoint for your page). It returns { token, expiresAt }.
Options
mountModule resolves with a handle once the piece is on screen:
Events
The composition editor and the widget creator add events of their own (
dirty, saved, canvasChanged, widgetPublished, limitReached and more), and say more in ready and error. They are listed in Canvas Embedding.
Several pieces on one page
mountModule creates a WidgeticUI, mounts one piece with it and destroys it again when you unmount(). To mount several pieces, or to download one ahead of time, make the WidgeticUI yourself:
Make the pieces look like your product
A piece can wear your colours, the roundness of your corners and your typeface. The components and the layout stay Widgetic’s: only the look follows your design system. The words can follow your language too (see below). Each piece has a theme of its own, so the File Browser, the composition editor and the widget creator can look the same or different.The settings
Your own
colors win over the preset’s. A primary of yours without a primaryForeground gets the white or the dark ink that reads better on it.
Writing a colour
#rrggbb, #rgb, rgb(11 107 203) and hsl(210 90% 41%) always work. A colour name or oklch() works in a browser that understands it. var(--brand) and currentColor are refused: the piece reads the colour when you give it and cannot look at your page’s variables from there, so read the value in your page and pass it in. Transparency is ignored. The piece warns in the console about a combination that is hard to read: your text under 4.5:1 against your background, or your primary colour (or the text on it) under 3:1.
A font your page does not load yet
A font can only be defined by the page itself, never from inside a piece. Give thehttps address of its stylesheet as stylesheetUrl and the piece adds one <link rel="stylesheet" data-widgetic-ui-font> to your <head>, once however many pieces use it. It stays after unmount(), like the font rules every piece adds. A font your page already loads needs no stylesheetUrl. Under a strict Content-Security-Policy, style-src has to allow the stylesheet’s host and font-src the font files (for Google Fonts, https://fonts.googleapis.com and https://fonts.gstatic.com).
Changing a look later
- A bare mode (
'dark',{ mode: 'dark' }) changes the mode and keeps the rest of the look. Settings replace the whole look. - Settings given in a mount call, or to
setThemeon a piece, are that piece’s own.ui.updateTheme(settings)does not take them away, and a page that changes a preset does not turn a dark piece light. A call that names a mode reaches every piece. ui.getTheme()tells you the page’s mode and settings.'system'stays'system'.- A theme that cannot be used is refused with the code
INVALID_THEME. The message names the setting and what to write instead, and nothing changes. When only the piece can tell (an unknown preset name, a colour it cannot read),ui.updateThemereports it in the console and the piece keeps its look, whilesetThemeand a mount call throw (the mount fails withMOUNT_FAILEDand leaves its container empty).
What a theme cannot do yet
- The widget creator takes a theme’s mode, neutral colours, brand colour and font, but not yet its corners. A dark
mode, or colours of your own forbackground,foreground,muted,mutedForegroundandborder, restyle its panels, toolbar, menus, dialogs and chat, and thefontapplies to all of its text. Every text of it stays readable in a dark look, for each built-in preset and for neutral colours of your own. Yourprimarycolour is the creator’s accent: the selected tool, the sliders, the buttons that act, the chat’s Send button and what is on them, darker under the pointer (lighter on a dark surface). The text and icons on an accent are yourprimaryForegroundwhen you give one, else white or a dark ink, whichever reads on your colour, so a light yellow gets dark text. Without aprimarythe accents are Widgetic’s own green and blue. The canvas board and the live widget previews stay light on purpose (they show the widget as your customers will see it), and the chat’s message bubbles keep their own light colours. The handles the canvas draws on its board, a few accents the creator sets in script and the colours of a status (the green of a published widget, the red of an error) do not take your brand colour, and most of its corners are Widgetic’s own, soradiuschanges very little of it. The console says what stays light when a creator gets a dark theme. The File Browser and the composition editor follow a dark theme. - No layout or icons. The components are Widgetic’s. The words of the File Browser and of the composition editor can be changed (see the next section); the widget creator is in English.
- Colours the piece can read as they are. No CSS variables, gradients or images.
- The old
ThemeConfigshape is gone.{ mode, colors: { secondary, surface, text }, spacing }never changed anything in a piece andthemeno longer takes it. Use the settings above.
Show the pieces in your customers’ language
The File Browser, the composition editor, the widget creator and Workflows can speak your customers’ language. Every word they show is a message with a key. You hand over the words you want to change, and every key you leave out stays English, so you can add a language one screen at a time. The examples are the File Browser’s; The composition editor, The widget creator and The Workflows screen say what is different.Languages that come with the pieces
Twelve languages are built in: French (fr), German (de), Spanish (es), Brazilian Portuguese (pt-BR), Dutch (nl), Danish (da), Swedish (sv), Simplified Chinese (zh-CN), Arabic (ar), Hebrew (he), Persian (fa) and Urdu (ur). When your locale is one of them, or a regional form of one (fr-CA reads French, pt and pt-PT read pt-BR, zh-Hans reads zh-CN, ar-EG reads Arabic, and the old tag iw reads Hebrew), the piece downloads that language’s words as one small file from the same folder of the CDN as the piece itself, and its first paint is already in that language. Any other locale (English, zh-TW, a tag nobody wrote words for) downloads nothing and the words stay English: plural forms, dates and direction still follow locale.
- Your
messageswin. The built-in words come first and yours replace them, key by key. - They are provisional. The built-in words are machine translations that no native speaker has read yet.
module.localePackStatus('de')says'provisional'(andnullfor a language with no words), andmodule.supportedLocaleslists the twelve. Tell support@widgetic.com about a word that is wrong, with the piece, thelocaleand the message key. - A file that cannot be downloaded is said once in the console, and the piece goes on in English.
Which keys there are
fileBrowser.module.defaultMessages is an object of every key and its English text: a few hundred messages, grouped by where the words appear (action., upload., dialog., preview. and so on). It always matches the version of the piece you load, and it is the list to hand to a translator.
Writing a message
A message is text.{name} is a value the File Browser puts in; a number is written the way the locale writes numbers. {count, plural, one {# file} other {# files}} picks the form that fits the number in your customer’s language: other is required, =0 {none} matches exactly 0, # is the number, and a language with more forms says so (Polish has one, few, many and other). \{, \}, \# and \\ write the character itself. A translation keeps the {names} of the English message, in any order, and cannot ask for a value the message is never given.
Checking your words
fileBrowser.module.checkMessages(messages) says what is wrong without showing anything: one sentence for each key that does not exist (with the nearest real one), value that is not text, message that does not parse, or {name} the message is never given. An empty list means every message will be used. Put it in a test of your page.
A message that cannot be used never breaks the screen: the English text of that key is shown and the console says so once. A value of the wrong shape (messages: 'Anuluj', locale: 5) is a mistake in your code and is refused where you wrote it: setLanguage throws a TypeError that names the option and changes nothing, and a mount call fails with MOUNT_FAILED and the TypeError as its cause.
Pop-ups
The File Browser answers a rename, a delete, a new folder and a pick with a small pop-up in the bottom corner of the box you gave it (the right one, or the left one for a language written right to left), in the piece’s theme and in your language. Their words are messages like the rest: thetoast. keys, with toast.regionLabel (the name of the region a screen reader announces them in) and toast.closeLabel (the name of the close button). After a pick the piece says “Files selected”, not the Widgetic site’s “Added 2 files to the editor”. The sentence under “Rename failed” or “Failed to delete” is your library’s own and is shown as it arrives.
Two habits come with the pop-up library. It moves focus to the newest pop-up when someone presses Alt+T anywhere on the page while one is shown, and a pop-up goes away by itself after about three seconds. You cannot move the pop-ups or switch them off yet.
The composition editor
The composition editor takes the samelocale, messages and setLanguage, and its module.defaultMessages and module.checkMessages(messages) work as above. One thing is different: one messages object covers the whole editor. The editor is made of three parts with words of their own, and it splits what you give between them, so you never need to know which part says a word:
- the editor’s own words, the
editor.keys: the Design and Content tabs, every control, the save button, the dates and the failures it explains; - the file picker that opens from its file fields, which is the File Browser: the keys above (
action.,upload.,dialog.and so on), with the samelocaleandmessages; - the rich text editor of its text fields, the
shadEditor.keys. The editor draws its own text toolbar, so these words are not on screen today and a translation can leave them out.
editor.module.defaultMessages lists them all, with their English text: more than 500. A key that is in none of the three is said once in the console as [Editor] ..., with the nearest real key, and ignored.
- Dates follow
localewithout your help. The names of the months and weekdays in its calendars, and the way a date is written, come from the browser. Only the names of the calendars and of their buttons (what a screen reader says) are messages. The date range control shows dates the way the language writes them: 7 October 2026 reads10/07/26withen(and with nolocale),07/10/26withen-GB,07.10.26withdeandpl, and26/10/07withja, so giveen-GBwhen you want the day first in English. A weekday heading in a calendar is the language’s own short name, not cut to two letters. In a language written right to left it is the one-letter name instead (Arabicح ن ث ر خ ج س), because the short names of Arabic are whole words that do not fit a column, and the whole name is the label of the heading for a screen reader. Where the browser has no one-letter names for a language (Urdu), the short names stay. Values saved in the composition stay structured dates, the same in every language. - A change of language reaches what is on screen. Labels and buttons change at once, and so do the banners of a composition that could not be opened and of a save that failed. A sentence the editor said once, such as why there is no preview, keeps its language until the editor says something new.
- Pop-ups from its file picker. A rename, a delete, a new folder and a pick raise a small pop-up inside the module’s box, in your language (
toast.keys, includingtoast.regionLabelandtoast.closeLabel). After a pick the module says “Files selected”, not “Added … to the editor”.
The widget creator
The widget creator takes the samelocale, messages and setLanguage, and its module.defaultMessages and module.checkMessages(messages) work as above. Like the composition editor, one messages object covers the whole creator. It is made of parts with words of their own, and it splits what you give between them, so you never need to know which part says a word:
- the creator’s own words, the
creator.keys: the widget’s panel, publishing and versions, the chat’s generation options (the language and, in debug mode, custom libraries), the limits it explains, the lines of its status bar; the preset Libraries toggle and its add-library control appear only in creator debug mode, which the widget creator module does not turn on, but their messages stay in the catalog for hosts that do; - the chat’s words (
chat.keys) and the canvas’s (canvas.keys: the toolbar, the zoom controls, the context menu, the properties panel, the words the shapes draw on the canvas); - the words of the starting-point picker that a customer with nothing yet is shown, the
presetPicker.keys (presetPicker.heading,presetPicker.intro,presetPicker.startFromScratchand so on), and the two words of the region its pop-ups are announced in,toast.regionLabelandtoast.closeLabel, which are the File Browser’s keys of the same names, so one translation serves both; - the composition editor with its file picker and its rich text editor (the
editor.keys, the File Browser’s andshadEditor.). The creator’s composition steps are off in a session, so these are on no screen today and a translation can leave them out.
creator.module.defaultMessages lists them all, with their English text: more than 1,300.
- Dates follow
localewithout your help. The date and time of a version are written the way the language writes them (10/01/2026 14:05in English,01.10.2026 14:05in Polish,2026/10/01 14:05in Japanese). Only the words around them are messages. - A change of language reaches what is on screen. The canvas, the chat, the panels, the menus, the picker and the pop-ups change at once, with no new mount. As for the other pieces,
setLanguagekeeps what you leave out and replaces the messages. presetLabelsstill works. It is a shortcut for the picker’s eight messages and sits undermessages: when both say a word,messageswins, and it stays in force aftersetLanguagefor the words the new messages leave out.- A mistake never breaks the screen. A key that none of the parts has is said once in the console as
[Creator] ..., with the nearest real key. A text that cannot be used is said once by the part that owns the key ([Creator],[Chat],[Canvas],[Editor],[FileBrowser],[ShadEditor], or[WidgetCreatorModule]for the picker and the pop-up region), and the English text of that message is shown.
The Workflows screen
Workflows takes the samelocale, messages and setLanguage, and its module.defaultMessages and module.checkMessages(messages) work as above. It has one messages object for all of it: a few hundred messages whose keys start with workflows., for the list, the editor and its panels, the names of blocks, operators and units, what is wrong with a workflow that cannot be published, what a failed request means, and the words of the screen that says a part could not be drawn. The words of a block are made of parts the screen puts together ({field} {value}, {count, plural, ...}), so a language that puts them in another order can say so in its own message. All twelve built-in languages come with these words. A key you leave out stays English.
Right to left
The File Browser, the composition editor, the widget creator and Workflows are read the way your customer’s language is written. For Arabic, Hebrew, Persian, Urdu and the other languages written from right to left, the whole screen turns around and a line starts on the right.setLanguage({ locale: 'ar' }) turns a piece that is on screen around, and setLanguage({ locale: 'de' }) turns it back, with no new mount.
- The File Browser. The list starts on the right, the search box, the sort and filter controls, the selection bar, the upload panel and the windows are laid out from the right, the close button of a window is in the corner where a line starts, menus open toward the end of the line, the pop-ups come up in the bottom left corner, and the arrows that mean back and forward are mirrored. In the preview, the arrow key that points toward the end of the line goes on to the next file.
- The composition editor. The settings panel is docked at the start of the line, on the right, and the preview on the other side, the same distance from the middle as in English. The Design and Content tabs run from the start of the line, the label of a control is at the start of its row, the calendars start their week at the start of the line, and sliders and switches run from the start. The file picker that opens from a file field is the File Browser above.
- The widget creator. The tools of the toolbar run from the start of the line, the properties panel is docked at the end of it, the menus, the list of shapes, the publish history and the starting-point picker are laid out from the start. The composition editor and the File Browser inside it are turned around the same way.
- Workflows. The list, the top bar of the editor, the tabs, the side panel, the menus and the dialogs are laid out from the start of the line, as a whole. The canvas is the exception: a workflow is read from its first block forward, so it is drawn from left to right in every language, and its lines run from a block on the left to the next one on its right.
- What a customer types. A text field, the tags of the editor, the chat and the text fields of the quick edit are read by their own first letter: a sentence in Latin letters is set from the left and one in Arabic from the right, side by side. A field with nothing in it yet takes the direction of the screen. An email, a link and a colour code stay left to right on every screen.
- Only
localedecides. The direction of your page does not: a page written right to left (<html dir="rtl">) leaves an English piece alone, and a page written left to right does not stop an Arabic one. Changingmessagesalone changes nothing about it. - Names. File and folder names are read by their own first letter, so a Latin name next to Arabic ones is not put in the wrong order. In a language written right to left, a name inside a sentence (“Failed to delete 2024 report.pdf”) sits between two invisible marks (Unicode isolates U+2068 and U+2069), so the words around it cannot turn it around. A screen reader does not say them; leave them out when a test compares the text of a pop-up.
- The video time line stays left to right, because time runs that way in every language.
- Drawings stay left to right. The drawing board of the widget creator, the live previews of widgets and the code are left to right in every language, and so are the drawings that mean “left” and “right” in the composition editor (the saturation and hue of the colour picker, the grid that places a box, the corners of a rounded box). A drawing is not a line of text, and a widget’s own preview must look the way its author made it.
- Two small things are open. The weekday names of an Urdu calendar are the short ones, which are whole words because the browser has no one-letter names for Urdu, so that row is tight. And the panel of a widget in the creator opens on the side of its shape that has more room, as it does in English, without knowing where the properties panel is: for a shape near the middle of the board it can open over that panel.
What stays as it is
- Words that belong to someone else. What your own media library says (the message of an error you throw), your file names and your library’s
label, the names of folders a customer made, and a sentence Widgetic’s API sends back are shown as they arrive. errorevents. Theirmessageis English: it is for your logs.- What is saved with a composition. The title of a new item (“Item 03”), “Untitled Composition” and the names a font weight or a position is saved under (“ExtraBold”, “top-left”) are written into the composition when they are made, so they stay English. The editor shows the weights and the positions in your language.
- The widget author’s words. The names, labels, descriptions and placeholders of a widget’s settings, its default texts and the names of typefaces are shown as the author wrote them.
- The team’s words in Workflows. The names of workflows and of compositions, the event names a team types into a trigger, and the names of the tables, columns and actions of a widget are shown as they were written.
- What the customer and the AI write. The prompts, the answers, the names of widgets and the code are the customer’s and the AI’s. In the widget creator, lines the creator saved into the chat stay English in the API; on screen it translates known lines (a failed generation, a restore) at display time. The
messageof itslimitReachedevent stays English (usecode). - What a session never draws. The widget creator’s sign-in screen, its standalone header, its debug panels, the preset Libraries section of the chat’s generation options, its list of keyboard shortcuts and its composition steps are not shown in a session, so they are not translated.
Tokens
getToken is the only credential the SDK takes. It is called with { reason }:
'initial': the piece is starting.'expiring': the current token runs out in about a minute. Return its replacement now.'expired': the current token has already run out (the page was asleep, or an earlier refresh failed).
{ token, expiresAt } (an ISO string, epoch milliseconds or a Date). With expiresAt the piece renews the token in time and the customer is never interrupted. The answer of your own endpoint can pass on what Widgetic gave you.
When your getToken answers an 'expired' call with the very token the API had refused, the piece takes it that you have no better one. It does not ask again for five seconds, whatever asks, so a session that was ended is reported once and not once per request.
An apiKey option is refused with API_KEY_NOT_ALLOWED. The key must stay on your server.
Workflows takes an editor session with the workflows surface, like the other pieces, or the token of a signed-in person, and asks for it in the same way.
Show your own media library
If your platform already has a media library (a CMS, an S3 bucket, a DAM), give the File Browser plain functions that read and write it. Your customers browse, upload and pick from your files with the same screen. The piece then never talks to Widgetic, so there is no session to make and nogetToken.
- A failure inside your functions is reported through the
errorevent withfatal: false. The File Browser shows the customer a list that cannot be read (with “Try again”), an upload that fails (in the upload panel) and arenameordeletethat your library refuses (in a pop-up, with your library’s sentence under it): the file stays as it was, and your page hears of it as well, so use the event for your log, not for a second message. The code isMEDIA_LIBRARY_plusUNAUTHORIZED,FORBIDDEN,NOT_FOUND,NETWORK,INVALID_RESPONSE,NOT_SUPPORTEDorFAILED, andactionsays what the customer was doing (load,upload,renameordelete). Throw an error with astatusproperty (a failedfetchresponse) and it is sorted for you. - Everything your functions return is checked before it is shown. A file without an id, a name or a usable address is skipped with a console warning rather than breaking the list.
- The
urlof a file is what a widget stores and shows. Once your customer saves or publishes, everyone who sees the widget loads that address, not only the customer who picked it: give one that stays valid and needs no sign-in. A link that expires after a few minutes, or one that checks a cookie, breaks the published widget. Widgetic keeps theidwith the file only so that the picker shows it as selected when it is opened again. - The composition editor and the widget creator take the same
mediaLibrary: their file pickers then show your files. - Text, PDF and font previews read the file with a script, so a file on another website needs an
Access-Control-Allow-Originheader for your page’s origin. Pictures, audio and video are shown without it.
Workflows
workflows is the screen where a team builds workflows: compositions chained together, with filters between them that decide which composition a visitor sees next. It opens on the list of the workspace’s workflows (search, sort, show or hide the archived ones, new, blank or from a starting point you offer, duplicate and delete in any state), and a workflow opens in the editor: a canvas of blocks that go forward only (up to 25), a side panel for the block that is selected, a draft that is saved while you work, Publish (a published version never changes) and Discard changes, the history of versions, a Test run that plays a visitor through the draft, a Runs tab that counts the visitors who went through each block, and Settings (time zone, embed code, pause, archive). What each version of the screen changes is in Versions & Changelog.
What a workflow does on a page is the work of the Embed SDK: a workflow only decides which composition the embed shows next, and the embed follows it from one more attribute on the tag you already use. See Show a path of compositions in one place.
To give the screen to your customers, one at a time, with an editor session, see Workflows. The rest of this section is true for a session and for a signed-in person alike, except where it says otherwise.
workspaceIdis required. It is'personal', or the id of the organization (the team) whose workflows are shown and edited. Workflows is for team accounts and partners: the API refuses any other workspace, and the screen says so. With an editor session it is always'personal': a session works in that one workspace, and the workflows it shows are its customer’s own. Any otherworkspaceIdis refused with403andEDITOR_SESSION_ROUTE_NOT_ALLOWED.getTokengives an editor session’s token, or the token of a signed-in person. For one of your customers it resolves with the token of an editor session made for them with theworkflowssurface (see Workflows): the screen then reaches that customer’s own workflows and compositions, and nothing else. For your own team it resolves with the token of a signed-in person who may work in that workspace (a member of the team, or your own account), the way the Widgetic app itself calls the API. An editor session that was made without theworkflowssurface is refused with403andEDITOR_SESSION_ROUTE_NOT_ALLOWED.getTokenis called once before the screen appears (agetTokenthat fails fails the mount), and again when the token is about to run out or the API refuses it (reason: 'expired', once per refusal), as in Tokens. The screen never stores a token, and no API key ever reaches the page.- Your page owns where the person is. The screen opens
workflowIdfirst, and the list when you leave it out. When the person opens a workflow or goes back to the list, the screen says so withopenWorkflow(workflowIdisnullfor the list), so your page can keep its address in step. When your page moves, as with the back button,workflows.module.setWorkflow(workflowId)moves the screen (nullfor the list). The screen writes nothing to your address bar, your storage or your cookies. - The embed code is yours to show. The screen has its Embed code button only when something listens for
showEmbedCodeat the moment it mounts, and only for a workflow that has been published. It tells your page which workflow, and your page shows the tag, because it knows how its own embed is built: the tag of the workflow’s first composition withworkflow-idadded, as in Show a path of compositions in one place. - Look and language are as for the other pieces.
theme,setTheme,locale,messagesandsetLanguagework as described in Make the pieces look like your product and Show the pieces in your customers’ language, the twelve built-in languages included, and a language written right to left turns the screen around. - Events.
ready,openWorkflowandshowEmbedCode(above), anderroras in Events.errorhasfatal: truewhen the screen could not start (andmountModulerejects as well). Withfatal: falsethe screen carries on:RENDER_FAILED(a part of it could not be drawn: the rest stays and the person is offered “Try again” in its place),REQUEST_FAILED(a request to the API failed: the screen tells the person by itself),SESSION_ENDED(the API no longer accepts the tokengetTokengives) andTOKEN_REFRESH_FAILED(a refresh of the token failed and will be tried again). - What it weighs. It is downloaded when you mount it, and only then: about 0.2 MB gzipped to start, and about 0.3 MB gzipped once all the parts of the editor have been loaded.
Content Security Policy
If your page sends a Content-Security-Policy, the File Browser works with this and nothing more. The build tests load it under the same policy, list, pick and upload images and preview a PDF; each source below is needed.img-src also needs the address your customers’ files are served from (the url of a picked file). To preview a PDF, that address also goes in connect-src (the PDF is downloaded with a request, so the file server has to allow your page’s origin with CORS) and the policy needs the worker-src blob: above; without it the preview says “Failed to load PDF” and everything else works. No data: in script-src and no 'unsafe-eval' are needed. The PDF viewer is downloaded when the first PDF is previewed, from the same folder of the CDN as the piece itself, so script-src needs nothing more.
- The composition editor works under the same policy plus
frame-src https://cdn.widgetic.com(the live preview is the widget’s published page). Its font picker also needsstyle-src https://fonts.googleapis.comandfont-src https://fonts.gstatic.com. - A font of your own (
theme.font.stylesheetUrl) needs its stylesheet’s host instyle-srcand its font files’ host infont-src. See A font your page does not load yet. - The widget creator needs what the composition editor needs plus
frame-src https://wbuilder.widgetic.comandconnect-src https://wbuilder.widgetic.com(the live previews of widgets are served from there). - Workflows needs less than the File Browser:
script-src,style-src 'unsafe-inline'andfont-src data:, plus two sources that the first version of the screen did not need.connect-srchas to allow the API (yourapiUrl,https://api.widgetic.comunless you run your own), andimg-src https://cdn.widgetic.comis for the pictures of compositions in the list and on the canvas. It needs noframe-src, noworker-src, no inline script and noeval. Together:default-src 'none'; script-src 'self' https://cdn.widgetic.com; connect-src https://cdn.widgetic.com https://api.widgetic.com; style-src 'unsafe-inline'; font-src data:; img-src https://cdn.widgetic.com. Theconnect-src https://cdn.widgetic.comis the SDK’s request for the manifest, as for every piece, and it is not made when you pin aversion. A page that allowed only what version 0.1.5 needed has to add the API and the pictures.
What the SDK puts on your page
- On import: nothing.
- While a piece downloads: one
div[data-widgetic-ui-loading]in your container, removed when the piece is up. - A mounted piece: one
<widgetic-ui-module>element in your container, holding a shadow root. The pieces also add one<style data-widgetic-ui-document-rules>to<head>with@font-facerules for fonts namedWidgetic UI …, so they cannot collide with yours. That<style>is shared by every piece on the page and never added twice, so it stays when a piece is unmounted. The composition editor also adds Google’s stylesheet for each font the customer looks at in its font picker; those<link>s go when the editor is unmounted. Your own<link>s and<meta>s are never touched. - Never: global CSS or CSS variables, changes to
<html>or<body>,postMessage, or removing your own elements.
React
mountModule the piece’s name ('composition-editor', 'widget-creator' or 'workflows') and its options. The Widgetic build tests run it in a React 18 page, with and without <React.StrictMode>, for all four pieces. The same pattern works in Vue and Svelte: mount in the component’s mount hook with an AbortController, and abort and unmount when it goes away. Mounting needs a page, so start it from an effect or an event handler: calling mountModule while rendering on a server rejects with NOT_IN_BROWSER.
Versions and self-hosting
Pieces are static files, so nothing can change under your feet when you pin a version:- Without
versionthe SDK reads the manifest and loads the newest version. Withversion: '0.1.0'it loads that file directly. Pin the version in production and upgrade when you have looked at the release. - Read what changed before you move. Every version has an entry in the changelog, and a version that can break your page is marked
breakingand is always a new major version (a new minor version while the number starts with0). See Versions & Changelog. - To serve the pieces yourself, copy that layout to your own origin (the whole
<version>folder:index.jsfetches itschunks/files from the folder it was loaded from) and setmoduleBaseUrl. The manifest’sentrymust be a.jsfile inside the piece’s own folder on the same origin. - Failed downloads are retried twice (network errors and server errors, not 404). A piece also asks twice more, the same way, when a file it fetches later fails. Requests to the CDN carry no cookies.
Errors
Every error carries acode.
Next Steps
Editor Sessions
Make the token on your server
Canvas Embedding
The widget creator and the composition editor
Managed Users
File your customers’ content under them
Credits & Billing
How the AI is paid for