SmarkForm FAQ
Below are answers to common questions about SmarkFormâs behaviour, especially around edge cases or features that might catch you off guard at first.
- SmarkForm Essentials
- What is SmarkForm?
- Do I need a
<form>tag? - How do I get and include SmarkForm?
- What is the
data-smarkattribute? - What are the different SmarkForm component types?
- What are triggers and actions?
- What is «context» in SmarkForm?
- What is the Singleton Pattern?
- How do I add a âclearâ button to a
colorornumberfield?
- Lists
- Data: Import, Export & Reset
- My exported JSON is missing some fieldsâwhatâs up?
- Why are my nested form fields named weirdly in the JSON?
- Why does my form export null values? How do I map them to HTML fields?
- Is there a
notNullattribute (orrequiredoption)? - How do I submit form data to a backend?
- Can I use a classic HTML form submission instead of JSON export?
- How do default values and reset work?
- Events & Actions
- Behaviour & Troubleshooting
- API & JavaScript
- Integration & Deployment
- Have a question not covered here?
SmarkForm Essentials
What is SmarkForm?
SmarkForm is a lightweight, markup-driven form controller for the web. You annotate your HTML with data-smark attributes and call new SmarkForm(element) â SmarkForm then enhances the subtree to support:
- Nested subforms that export plain JSON objects.
- Variable-length lists that export JSON arrays and can grow/shrink at runtime.
- Smart import/export of any JSON structure, no matter how deeply nested.
- Context-driven hotkeys for keyboard-driven interaction.
- Actions & events for custom behaviour without writing DOM manipulation code.
It has no runtime dependencies and ships as a single 49KB bundle (ESM or UMD). It is not a React/Vue/Angular component library â it is a DOM-first library that works best with server-rendered or static HTML.
See the Introduction and Showcase for a fuller picture.
Do I need a <form> tag?
No. SmarkForm works with any block-level element â <div>, <section>, <fieldset>, a plain <form>, etc. The root element is just the DOM node you pass to new SmarkForm(element).
<!-- All of these are valid SmarkForm roots -->
<div id="myForm">âŠ</div>
<section id="myForm">âŠ</section>
<form id="myForm">âŠ</form>
If you do use a <form> tag, SmarkForm does not intercept the browserâs native submit event â use an export trigger and AfterAction_export instead.
See How do I submit form data to a backend?.
How do I get and include SmarkForm?
The quickest way is a CDN import â no build step needed:
<!-- UMD: adds SmarkForm as a global variable -->
<script src="https://cdn.jsdelivr.net/npm/smarkform/dist/SmarkForm.umd.js"></script>
<!-- or as an ES module -->
<script type="module">
import SmarkForm from "https://cdn.jsdelivr.net/npm/smarkform/dist/SmarkForm.esm.js";
</script>
For production, pin to a specific version (e.g.
.../smarkform@0.5.0/dist/SmarkForm.esm.js) so your app isnât affected by future changes.
You can also install from npm and use with any bundler:
npm install smarkform
import SmarkForm from "smarkform";
See Getting SmarkForm for all distribution formats (ESM, UMD, CDN, npm, GitHub clone) and more details.
What is the data-smark attribute?
data-smark is how you tell SmarkForm which HTML elements to manage. Any element inside your root node without this attribute is simply ignored by SmarkForm.
The value can be empty (SmarkForm infers everything from context) or a JSON object with configuration options:
<!-- Empty: type inferred from the <input> tag -->
<input type="text" name="city" data-smark>
<!-- JSON options: explicit type, name, and a default value -->
<div data-smark='{"type":"form","name":"address","value":{"city":"Barcelona"}}'>
âŠ
</div>
<!-- Trigger: the "action" key marks this as a trigger button -->
<button data-smark='{"action":"export"}'>Save</button>
See Core Concepts â The data-smark attribute for the full syntax reference.
What are the different SmarkForm component types?
Every element with a data-smark attribute becomes a component of a specific type. The main types are:
Structural types (hold collections of fields):
| Type | Role | Exports |
|---|---|---|
form | Groups fields into a named subform | JSON object |
list | Variable-length list of repeated items | JSON array |
Scalar field types (hold a single value):
| Type | Role | Exports |
|---|---|---|
input | Default for any <input>, <textarea>, or <select> not otherwise matched | String |
number | Numeric field â imports/exports a JavaScript number, not a string | Number or null |
color | Color picker â allows null (unlike native <input type="color"> which always has a value) | #rrggbb string or null |
radio | Group of radio buttons sharing the same name treated as one field; nullable | String or null |
date | Date field with input sanitisation and standardised output format | ISO date string or null |
time | Time field with input sanitisation and standardised output format | ISO time string or null |
datetime-local | Date-and-time field with the same sanitisation/format guarantees | ISO datetime string or null |
Non-field component types:
| Type | Role |
|---|---|
trigger | Button that invokes an action (no field value) |
label | Smart label that auto-connects to its sibling field |
SmarkForm infers the type from context in most cases:
<input>,<textarea>,<select>âinput(unless a more specific type is inferred from the elementâstypeattribute, e.g.type="color"âcolor)<label>âlabel- A
data-smarkwith an"action"key âtrigger - Explicit
"type":"form"or"type":"list"for container elements
See Core Component Types for the full reference.
What are triggers and actions?
A trigger is a button (or any focusable element) with an "action" key in its data-smark options. When clicked (or its hotkey is pressed), it invokes the named action on the appropriate component.
<button data-smark='{"action":"export"}'>Save</button>
<button data-smark='{"action":"clear"}'>Clear</button>
<button data-smark='{"action":"reset"}'>Reset</button>
<!-- List-only actions: -->
<button data-smark='{"action":"addItem"}'>Add row</button>
<button data-smark='{"action":"removeItem"}'>Remove row</button>
Not all actions are available on all component types â for example, addItem and removeItem are exclusive to list components. The common actions (export, import, clear, reset) are available on all field components. You can also define custom actions.
See Quick Start â Actions and Triggers for a hands-on introduction.
What is «context» in SmarkForm?
The context of a trigger is the component that receives the action. By default, SmarkForm resolves it automatically â the triggerâs ânatural contextâ is its closest ancestor component that implements the action.
This means:
- A
clearbutton placed directly inside a list itemâs template clears that list item. - If the button is inside a nested subform, it clears the subform.
- If it is outside all lists and subforms, it clears the enclosing component â which might be a
form, alist, or even a scalar field likecolorornumberif the button is embedded inside one using the singleton pattern (see What is the Singleton Pattern?).
For the case where no convenient ancestor exists, you can always target any component explicitly with the "context" property and a relative path:
<!-- Clears only the "email" field, no matter where the button is placed -->
<button data-smark='{"action":"clear","context":"email"}'>Clear email</button>
See Quick Start â Actions and Triggers and Form Traversing for full details.
What is the Singleton Pattern?
The Singleton Pattern is a technique where you wrap a single HTML form field inside a non-field container tag and declare that container as the SmarkForm component. The container becomes the component; the inner <input> (or <textarea> or <select>) is just the rendering surface.
<!-- Without Singleton Pattern â bare input, no room for sibling triggers -->
<input type="color" name="bgcolor" data-smark>
<!-- With Singleton Pattern â container IS the component; has room for extras -->
<span data-smark='{"type":"color","name":"bgcolor"}'>
<input data-smark>
<button data-smark='{"action":"clear"}'>â Reset</button>
</span>
Because the trigger (clear) is now inside the component, its natural context is the color component itself â no explicit context path needed.
The Singleton Pattern is available for all scalar field types (input, number, color, radio, date, time, datetime-local). It is especially useful for:
- Embedding a
clearbutton directly inside acolorornumberfield. - Adding per-item
removeItembuttons inside lists of scalars. - Grouping a label and its field as a single named component.
See Core Component Types â The Singleton Pattern for a full walkthrough.
How do I add a âclearâ button to a color or number field?
Native <input type="color"> always has a value â it cannot be empty â so SmarkForm provides its own color type that can export null. But how do you clear it without the keyboard?
Option A â Singleton Pattern (recommended): wrap the <input> in a container declared as the SmarkForm component, then place the clear trigger inside that container. The triggerâs natural context becomes the color component:
<span data-smark='{"type":"color","name":"bgcolor"}'>
<input data-smark>
<button data-smark='{"action":"clear"}'>â Clear</button>
</span>
Option B â Explicit context: keep the bare <input> and place the button anywhere else, but specify the target field by name:
<input type="color" name="bgcolor" data-smark>
<button data-smark='{"action":"clear","context":"bgcolor"}'>â Clear</button>
Option A is preferred because it is resilient to future renames â you only need to change the name in one place (the container) rather than in both the <input> and the buttonâs context path.
The same technique applies to number, date, time, and any other scalar type that you want to associate with extra UI controls.
Lists
Why canât I remove items from my list sometimes?
SmarkForm enforces a min_items limit (default is 1) on variable-length lists to keep them functional. When you hit this minimum, the âremoveâ button (data-smark='{"action":"removeItem"}') is automatically disabled. Once you add more items, it re-enables.
Check your list container â if itâs at min_items, thatâs why. If you rather want to allow empty lists, set {"min_items": 0}.
Add a disabled CSS rule (e.g.
button:disabled { opacity: 0.5; }) to make this state more obvious to users.
Why does my «add» button stop working?
Similar to removal, SmarkForm respects a max_items limit if you set one (e.g. {"max_items": 5}). When the list hits this cap, the âaddâ button (data-smark='{"action":"addItem"}') disables itself until you remove an item.
Style the disabled state with CSS or raise
max_itemsin your config if you need more slots.
My list wonât let me add items until I fill the current onesâis that intended?
SmarkForm does not block adding new items based on whether existing ones are filled. If your âaddâ button is disabled, check whether youâve hit max_items or whether a custom event handler is interfering.
How do I add animations to list items?
SmarkForm applies CSS classes during add/remove transitions, giving you a hook for animations:
- When a new item is added, SmarkForm briefly adds a configurable class (e.g.
new_item) to the new element â attach a CSS animation to âslide it inâ. - When an item is removed, SmarkForm adds another configurable class (e.g.
removing_item) and waits for any running CSS transition to finish before removing the DOM node â attach your âslide outâ animation to that class.
The exact class names can be configured in the listâs data-smark options.
See Showcase â Animations for a working, copy-pasteable example.
Data: Import, Export & Reset
My exported JSON is missing some fieldsâwhatâs up?
By default, SmarkForm skips empty items in variable-length lists when exporting to JSON. If you want those empty items included (e.g. "" or null), set {"exportEmpties": true} in your list config.
See the Showcase for a worked example.
Why are my nested form fields named weirdly in the JSON?
SmarkForm reflects the nesting structure in the exported JSON. A subform named "address" containing a field "city" will export as {"address": {"city": "âŠ"}}.
If you see unexpected nesting, double-check the name values in your data-smark attributes.
Why does my form export null values? How do I map them to HTML fields?
SmarkForm uses null to represent âthe user has not provided a valueâ â this is intentional and distinct from an empty string "".
Why null exists:
- Native
<input type="color">canât be empty; SmarkFormâscolortype returnsnullwhen cleared. - Date, time, number, and similar fields also return
nullwhen empty rather than""or0, to avoid ambiguity in the exported JSON. - Radio buttons return
nullwhen no option is selected. They also can be de-selected (by clicking the selected option again or by pressingDeletekey).
Mapping null to HTML fields on import: when a null value is imported into a field, SmarkForm clears the field to its native empty state. The clear action does the same thing programmatically.
Is there a notNull attribute (or required option)?
No â and there intentionally never will be one at the SmarkForm level.
Here is why: SmarkForm cannot tell whether a field is null because the user deliberately left it empty or because it was never touched. Enforcing non-null at export time would therefore silently coerce data that might be legitimately absent.
The correct approach is validation before submission: if a field must not be empty, prevent the user from triggering the export action until they have filled it in. You can do this with a BeforeAction_export event handler:
myForm.on("BeforeAction_export", ({ data, preventDefault }) => {
if (data.color === null) {
alert("Please pick a color before saving.");
preventDefault(); // cancel the export
}
});
HTML5âs own required attribute can also help for plain text inputs â those participate in the browserâs built-in constraint validation, which SmarkForm does not interfere with.
How do I submit form data to a backend?
The recommended pattern is:
- Add an
exporttrigger button to your form. - Listen to
AfterAction_exportto receive the data and send it to your server.
<button data-smark='{"action":"export"}'>đŸ Save</button>
myForm.on("AfterAction_export", async ({ data }) => {
await fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
});
See Event Handling â Common Patterns for more detail.
Can I use a classic HTML form submission instead of JSON export?
SmarkForm currently works with JSON-based import/export rather than native <form> POST submission. The <form> tag itself is not enhanced to submit via the browserâs built-in mechanism.
Current workaround: use AfterAction_export to send the JSON payload to your backend via fetch (see above).
đ Planned feature:
<form>tag enhancement â allowing native form submission or a transparent bridge to the Fetch API â is on the roadmap. This is not yet implemented.
How do default values and reset work?
The value property in a componentâs data-smark options sets its defaultValue. Calling reset() (or clicking a Reset trigger) restores the component to that default.
<!-- Name field defaults to "Alice" -->
<input data-smark='{"name":"name","value":"Alice"}' type="text">
For a form or list, you can set the default on the container:
<div data-smark='{"name":"demo","value":{"name":"Alice"}}'>
<input data-smark type="text" name="name">
</div>
import() updates the default by default. When you call import(data), the imported data becomes the new default for subsequent reset() calls. This means âLoadâ and âResetâ naturally work together: after loading new data, Reset brings you back to that loaded data.
// After this import, reset() will restore {name: "Bob"}
await myForm.import({name: "Bob"});
// Temporarily change value
await myForm.import({name: "Temp"}, {setDefault: false}); // won't update default
// Reset restores {name: "Bob"}, not the original initialization default
await myForm.reset();
To prevent an import from changing the default, pass setDefault: false:
await myForm.import(someData, {setDefault: false});
// reset() will still restore the previous default
The same option works from HTML triggers:
<button data-smark='{"action":"import","setDefault":false}'>Preview</button>
Importing undefined (which is what reset() does internally) never updates the default â it simply restores the current default value.
How the new default is computed: When
setDefault: trueand data is notundefined, SmarkForm performs a silentexportwithexportEmpties: trueand stores the result as the newdefaultValue. This guarantees that the stored default is a normalized, consistent representation of the imported state â including empty list items that would otherwise be stripped.
Events & Actions
I added an event listener, but itâs not firingâwhy?
SmarkForm supports events like AfterAction_export, AfterAction_addItem, AfterAction_removeItem, and more. Check:
- The event name â it must match the action exactly, e.g.
"AfterAction_addItem"(not"AfterAction_add"). - Scoping â
myForm.on(âŠ)listens to all descendants;myForm.onLocal(âŠ)listens only on that specific component. - Timing â handlers registered after the action fires are too late; register them during initialization.
See the Event Handling page for the full reference.
How do I add custom actions?
Pass a customActions object to the SmarkForm constructor. Each key becomes an action name that can be referenced in data-smark trigger buttons:
const customActions = {
async myAction(data, options) {
console.log("Custom action triggered with data:", data);
},
};
const myForm = new SmarkForm(document.getElementById("myForm"), {
customActions
});
<button data-smark='{"action":"myAction"}'>Run Custom Action</button>
Custom actions are bound to the root form instance (this inside the function refers to the root SmarkForm). They do not automatically participate in the BeforeAction/AfterAction event cycle unless you emit those events yourself.
How do I add keyboard shortcuts (hotkeys)?
Add a hotkey property to any triggerâs data-smark object. The value is the KeyboardEvent.key string for the desired key (single printable characters are most portable). Hotkeys are activated with Ctrl + the key:
<button data-smark='{"action":"addItem","hotkey":"+"}'>Add</button>
<button data-smark='{"action":"removeItem","hotkey":"-"}'>Remove</button>
<button data-smark='{"action":"export","hotkey":"s"}'>Save</button>
Hotkey reveal: when the user presses and holds Ctrl, SmarkForm sets a data-hotkey attribute on each currently active trigger button. You add CSS to turn that attribute into a visible badge â SmarkForm intentionally leaves the visual style entirely up to you:
/* Show a small "Ctrl+X" badge above each active hotkey trigger */
[data-hotkey] {
position: relative;
}
[data-hotkey]::after {
content: "Ctrl+" attr(data-hotkey);
position: absolute;
top: -1.4em;
left: 50%;
transform: translateX(-50%);
font-size: 0.7em;
background: #333;
color: #fff;
padding: 1px 4px;
border-radius: 3px;
white-space: nowrap;
}
When Ctrl is released the data-hotkey attributes are removed and the hints disappear automatically.
Context sensitivity: When a hotkey combination is pressed, SmarkForm searches for matching triggers across the focused component, all its ancestors, and the siblings of those ancestors â ordered by proximity to the focused element. The nearest match wins.
This means you can re-use the same key at different nesting levels: Ctrl+- can remove a phone when focus is inside the phones list, and remove a whole user when focus is at the user level. SmarkForm picks the right trigger automatically based on where the keyboard focus is.
See Hotkeys for full details and examples.
What if I want to reach an outer action with the same hotkey?
Use Alt+Ctrl instead of Ctrl to reach the next matching trigger up the hierarchy.
Following the previous example, if you have Ctrl+- to remove a phone and you want to remove the whole user instead, press Alt+Ctrl+- to skip the first match and trigger the next one.
This is well explained of the end-user guide â đ feel free to link it in your UI or documentation to help your users discover this kind of features.
Behaviour & Troubleshooting
Whereâs the error message when something goes wrong?
SmarkForm favors silent prevention over loud errors. For example, hitting min_items disables âremoveâ instead of throwing an error. You can hook into events (e.g. AfterAction_removeItem) to add custom feedback.
myForm.find('/foo/bar') returns null but the field exists
This almost always means find() was called before the form finished rendering.
SmarkForm builds its internal map of field components asynchronously during render(). Before rendering is complete, find() will return null (or undefined) for every path â even valid ones.
Solution: await the rendered promise before calling find():
const myForm = new SmarkForm(document.getElementById("myForm"));
await myForm.rendered; // wait for full render
const field = myForm.find("/foo/bar"); // now safe
Inside event handlers (e.g. AfterAction_*) this is not necessary because those events are only emitted after rendering is complete.
Never call
find()in the same synchronous tick as theSmarkFormconstructor â even for the root formâs own fields. Alwaysawait myForm.renderedfirst.
What does await myForm.rendered do?
The rendered property is a Promise that resolves once SmarkForm has finished building its internal component tree from the DOM. Rendering is asynchronous because SmarkForm processes data-smark attributes asynchronously to keep the browser responsive for large forms.
Any code that needs to access components via find(), or that needs to programmatically import() initial data, must wait for this promise:
const myForm = new SmarkForm(document.getElementById("myForm"));
// Wait before touching the component tree
await myForm.rendered;
// Now safe to use
const field = myForm.find("/address/city");
await myForm.import({ address: { city: "Paris" } });
Event handlers registered via myForm.on(âŠ) are always called after rendering is complete, so you do not need to await myForm.rendered inside them.
API & JavaScript
Can I have multiple independent SmarkForm forms on a page?
Yes, with a few guidelines:
- Do not nest one SmarkForm root inside another. SmarkForm forms should be independent siblings in the DOM.
<!-- Two independent root forms on the same page --> <div id="formA">âŠ</div> <div id="formB">âŠ</div> <script> const formA = new SmarkForm(document.getElementById("formA")); const formB = new SmarkForm(document.getElementById("formB")); </script> - One root with named subforms is often a better pattern than multiple roots â use
data-smark='{"type":"form","name":"billing"}'to create logically separate sections within a single root.<!-- One root form with named subforms --> <div id="outerForm"> <div data-smark='{"name":"formA"}'>âŠ</div> <div data-smark='{"name":"formB"}'>âŠ</div> </div> <script> (async ()=>{ const outerForm = new SmarkForm(document.getElementById("outerForm")); await myForm.rendered; const formA = outerForm.find("/formA"); const formB = outerForm.find("/formB"); })(); </script> - With the second pattern, absolute paths start from the real root. But this is more an advantage than a limitation since both forms can access the data of each other (if needed).
Whatâs this «API interface» I keep hearing about?
The API interface is a planned future feature for dynamic data â think fetching options for a <select> component from a server, or loading list items on demand. It is not yet implemented.
Stay tuned â details will land in the docs when itâs ready. See the Roadmap for more context.
Integration & Deployment
Which browsers does SmarkForm support?
SmarkForm targets the browsers covered by its Babel configuration and is tested with Playwright across Chromium, Firefox, and WebKit (Safari engine).
In practice this means modern versions of:
| Browser | Engine |
|---|---|
| Chrome / Edge | Chromium |
| Firefox | Gecko |
| Safari / iOS Safari | WebKit |
Older browsers (IE 11, legacy Edge) are not supported.
Can I use SmarkForm in React (or Vue, Angular, etc.) projects?
SmarkForm is not designed for virtual-DOM frameworks and the fit is awkward.
SmarkForm is a DOM-first library. It reads data-smark attributes from real DOM nodes at initialisation time and keeps its own internal component tree in sync with those nodes. That model sits at odds with how React, Vue, and Angular work.
Anyway, if you want to try it, please give us feedback on your experience.
Where does SmarkForm really shine?
SmarkForm is a DOM-first, markup-driven library â and that is a feature, not a limitation. It is an ideal fit anywhere you own the HTML and want powerful form behaviour without pulling in a heavyweight framework.
Server-rendered HTML stacks
If your backend renders HTML (Ruby on Rails, Django, Laravel, Symfony, plain PHP, ASP.NET Razor, Go templatesâŠ), you already own every DOM node before the page loads. SmarkForm slots in with a single <script> import and a new SmarkForm(el) call â no build pipeline required.
<!-- Drop this at the bottom of your server-rendered page -->
<script type="module">
import SmarkForm from "https://cdn.jsdelivr.net/npm/smarkform/dist/SmarkForm.esm.js";
const myForm = new SmarkForm(document.getElementById("myForm"));
myForm.on("AfterAction_export", ({ data }) => {
fetch("/submit", { method: "POST", body: JSON.stringify(data) });
});
</script>
Your backend devs keep writing plain HTML templates; SmarkForm upgrades them silently.
Static-site generators and JAMstack
Hugo, Jekyll, Eleventy, Astro (static output), Zola⊠â all generate HTML at build time. SmarkForm enhances those static pages with dynamic form behaviour without changing the rendering pipeline. The docs site youâre reading right now is a Jekyll site that uses exactly this pattern.
Progressive enhancement of existing pages
Got a legacy app or a CMS (WordPress, Drupal, TYPO3, Joomla) where you can add data-smark attributes but canât change the server stack? Add a small <script type="module"> block or enqueue the UMD bundle, point it at an existing <form> or <div>, and the page instantly gains subforms, variable-length lists, and JSON export â all without touching server code.
Alpine.js and âHTML-over-the-wireâ stacks
Alpine.js, HTMX, Hotwire/Turbo, and similar tools share SmarkFormâs philosophy of keeping behaviour close to the markup. They pair naturally:
- HTMX / Turbo replace page fragments with server-rendered HTML; SmarkForm re-initialises on the new fragment via the
DOMContentLoadedequivalent or a simple mutation observer. - Alpine.js can call
myForm.export()/myForm.import()from its event handlers, letting Alpine manage UI state while SmarkForm owns form data.
Vanilla JavaScript projects and micro-frontends
When you donât want any framework at all â a standalone widget, an embeddable form component, a micro-frontend â SmarkForm gives you complex form logic (nested subforms, dynamic lists, hotkeys) in a single ~38 KB bundle with zero runtime dependencies.
Back-office tools and internal dashboards
Internal tooling rarely needs a polished React stack. A lightweight HTML page plus SmarkForm can handle configuration editors, data-entry screens, and admin panels with far less boilerplate than a full SPA framework, and with no Node.js build step if you use the CDN.
In short
| Environment | Why SmarkForm fits well |
|---|---|
| Rails / Django / Laravel / PHP | You own the HTML; one <script> tag is all you need |
| Jekyll / Hugo / Eleventy | Static HTML at build time + dynamic forms at runtime |
| WordPress / Drupal / CMS | Add data-smark attributes; no server changes required |
| HTMX / Turbo / Alpine.js | Shared âHTML is the source of truthâ philosophy |
| Vanilla JS widgets | Zero deps, tiny bundle, pure DOM API |
| Internal dashboards | No build pipeline needed; rapid iteration |
Not sure whether SmarkForm is the right fit for your project? Open a discussion â weâre happy to help you evaluate!
Have a question not covered here?
You have a question that isnât answered here? You found a confusing edge case? You want to know if SmarkForm is a good fit for your project?
Open a discussion on GitHub or reach out at the Telegram community group. Weâre happy to help you out and may even add your question to this FAQ if itâs a common one!