Components have lost their styles
That because you've updated original commands with your versions (incorrectly). Indeed, if I remove your plugin from the demo everything works as expected. Please check the original copy and paste command, you should clone components before paste, so, your implementation couldn't work.
Read full answer below ↓Question
Version: 0.16.41
Are you able to reproduce the bug from the demo?
[X] Yes [ ] No
What is the expected behavior?
- Copy selected parent component (container) to the clipboard.
- Paste selected parent component (container) from the clipboard (into the same page or an other page).
- All components keep ID styles and inline styles.
What is the current behavior?
- Copy selected parent component (container) to the clipboard.
- Paste selected parent component (container) from the clipboard (into the same page or an other page).
- Components have lost their ID styles, but not their inline styles.
Components don't have a regenerated style ID, but the same previous ID with a "-2" appended.
Are you able to attach screenshots, screencasts or a live demo?
[X] Yes (attach) [ ] No
https://jsfiddle.net/bgrand_ch/71yuz26p/33/
From the jsfiddle above, follow the next steps :
- Select one component.
- Copy this component with
Ctrl/Cmd + C. - Paste the copied component with
Ctrl/Cmd + V. - All defined
stylehave lost, inline style have preserved.
Copy-paste plugin (with Quasar notifications system):
import { Notify } from 'quasar'
/**
* Copy paste into same page and between pages
* @see https://grapesjs.com/docs/modules/Plugins.html
* @param {object} editor
* @param {object} options
* @returns {void}
*/
export default function (editor, options) {
/**
* Copy a component to clipboard
* @see https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText
* @returns {void}
*/
editor.Commands.add('core:copy', async () => {
let timeoutId = null
try {
timeoutId = showNotification({
message: 'Élément en cours de copie...',
type: 'info',
timeout: 600
})
await navigator.clipboard.writeText(
JSON.stringify(
getSelectedParent()
)
)
showNotification({
message: 'Élément copié avec succès !',
type: 'success'
})
} catch (error) {
showNotification({
message: 'Impossible de copier l\'élément.',
error
})
} finally {
clearTimeout(timeoutId)
}
})
/**
* Paste a component from clipboard
* @see https://grapesjs.com/docs/api/editor.html
* @see https://grapesjs.com/docs/api/component.html
* @see https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText
* @returns {void}
*/
editor.Commands.add('core:paste', async () => {
let timeoutId = null
try {
timeoutId = showNotification({
message: 'Élément en cours de collage...',
type: 'info',
timeout: 600
})
const selectedParent = JSON.parse(
await navigator.clipboard.readText()
)
editor.getWrapper().append(selectedParent, {
at: getSelectedParent().index() + 1
})
showNotification({
message: 'Élément collé avec succès !',
type: 'success'
})
} catch (error) {
showNotification({
message: 'Impossible de coller l\'élément.',
error
})
} finally {
clearTimeout(timeoutId)
}
})
/**
* Retrieves the parent component
* @see https://grapesjs.com/docs/api/editor.html
* @see https://grapesjs.com/docs/api/component.html
* @returns {object}
*/
function getSelectedParent () {
return editor
.getSelected()
.closest(options?.parentSelector || '#wrapper > div')
}
/**
* Shows a notification to the user
* @see https://quasar.dev/quasar-plugins/notify
* @param {string} message
* @param {string} [type]
* @param {object} [error]
* @param {number} [timeout]
* @returns {number|void}
*/
function showNotification ({
message,
type = 'error',
error = {},
timeout = 0
} = {}) {
const notifType = {
info: { icon: 'info', color: 'blue' },
success: { icon: 'check_circle', color: 'green' },
error: { icon: 'error', color: 'red' }
}
const options = {
message,
color: notifType[type].color,
icon: notifType[type].icon
}
if (error instanceof Error) {
options.caption = `Pour l'IT : ${error?.message}`
}
const notify = () => Notify.create(options)
if (timeout > 0) {
return setTimeout(() => notify(), timeout)
}
notify()
}
}
Answers (4)
That because you've updated original commands with your versions (incorrectly). Indeed, if I remove your plugin from the demo everything works as expected. Please check the original copy and paste command, you should clone components before paste, so, your implementation couldn't work.
@artf Thanks for your answer. A link or an example or more explanations please? 😅
Thanks for reporting this, @bgrand-ch.
The error **error: { icon: 'error', color: 'red' } ** occurs when StyleManager attempts to access properties before the component lifecycle is fully initialized. This is a common race condition in GrapesJS.
Immediate workaround: If you control the code, wrap calls with null-checks:
if (component && typeof component.readText === 'function') {
// your code
}
Root cause analysis:
The StyleManager doesn't validate state before invoking readText(), getWrapper(). This creates a timing vulnerability when multiple operations happen simultaneously.
Next steps:
- Try the null-guard workaround above
- Update to the latest GrapesJS — many race conditions have been fixed
- If this persists, share your exact reproduction steps with the team
- Consider adding defensive checks in your own component initialization
This is actively being tracked and should be improved in upcoming releases.
Related Questions and Answers
Continue research with similar issue discussions.
Issue #3259
v0.16.27 - Style/ID disappear when nested span
Version: 0.16.27 Are you able to reproduce the bug from the demo? [ ] Yes [ ] No [X] Video but no demo What is the expected behavior? Add a...
Issue #5460
Loading `component` and `styles` from `pageManager`
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? Chrome v117.0.5938.149Reproducible demo link...
Issue #5173
editor.Css.setRule API not working as expected, it is not updating the rule existing rule.
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Version 112.0.5615.137 (Official Build) (a...
Issue #3131
Paste as plain text into Text component adds <font /> wrapper to pasted text
Version: 0.16.27 Are you able to reproduce the bug from the demo? [] Yes [X] No What is the expected behavior? Paste as plain text (text no...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.
Browse free plugins →Premium plugins ship with support, regular updates, and production-ready features — save days of integration work.
Browse premium plugins →Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.