Summary
When integrating DataTables ColumnControl (or other extensions) with Symfony Asset Mapper, the recommended pattern of dynamically importing CSS inside an async try/catch block silently fails — the CSS is never loaded.
Pattern that fails
try {
await import('datatables.net-columncontrol-bs5');
await import('datatables.net-columncontrol-bs5/css/columnControl.bootstrap5.min.css');
dtPlugins.columnControl = true;
} catch (error) {
console.warn('ColumnControl unavailable', error);
}
Symfony Asset Mapper uses native ES modules and a static importmap. It only processes static top-level import statements at build time. Dynamic import() expressions inside async functions are not analysed for CSS, so the CSS file is never injected into the page.
The JS loads fine (the extension initialises), but without its stylesheet the controls render incorrectly — in our case, the ColumnControl search input was invisible because none of the dtcc-* CSS rules were applied.
Fix
Move the CSS import to a static top-level import, separate from the conditional JS load:
// Static — processed by Asset Mapper at build time
import 'datatables.net-columncontrol-bs5/css/columnControl.bootstrap5.min.css';
// Dynamic — still fine for JS (optional/conditional loading)
try {
await import('datatables.net-columncontrol-bs5');
dtPlugins.columnControl = true;
} catch (error) {
console.warn('ColumnControl unavailable', error);
}
Context
Suggestion
The docs/examples for optional extensions could note that CSS imports must be static when using bundler-free setups like Symfony Asset Mapper, or provide a <link> tag fallback alongside the JS example.
Summary
When integrating DataTables ColumnControl (or other extensions) with Symfony Asset Mapper, the recommended pattern of dynamically importing CSS inside an async try/catch block silently fails — the CSS is never loaded.
Pattern that fails
Symfony Asset Mapper uses native ES modules and a static importmap. It only processes static top-level
importstatements at build time. Dynamicimport()expressions inside async functions are not analysed for CSS, so the CSS file is never injected into the page.The JS loads fine (the extension initialises), but without its stylesheet the controls render incorrectly — in our case, the ColumnControl search input was invisible because none of the
dtcc-*CSS rules were applied.Fix
Move the CSS import to a static top-level import, separate from the conditional JS load:
Context
importmap.json/importmap.php— no bundler (webpack/vite)Suggestion
The docs/examples for optional extensions could note that CSS imports must be static when using bundler-free setups like Symfony Asset Mapper, or provide a
<link>tag fallback alongside the JS example.