Skip to content

Feature/optimize editor performance#7793

Open
sempostma wants to merge 41 commits into
decaporg:mainfrom
laikacms:feature/optimize-editor-performance
Open

Feature/optimize editor performance#7793
sempostma wants to merge 41 commits into
decaporg:mainfrom
laikacms:feature/optimize-editor-performance

Conversation

@sempostma

Copy link
Copy Markdown
Contributor

Note

I had to close and ropen #7719 because I moved my decap-cms fork repository to an organization.

This PR simply prevents a rerender of the entire EditorControl tree. This is done by making all the props stable, aka not passing new references when its not needed.

The risk with this change is that it's very easy for someone to create a new commit that reintroduces in an unstable prop which would cause the same performance issues again.

Summary

I'm using Decap for a project with a very large collection schema with reorder-able blocks and lists. On a 2024's machine, typing is almost impossible so not acceptable as a deliverable.

Test plan

The best way to test it is adding a 'shouldComponentUpdate' to the editorControl:

  shouldComponentUpdate(nextProps, nextState) {
    const valuesThatWhereUpdated = []
    for (const key in nextProps) {
      if (this.props[key] !== nextProps[key]) {
        valuesThatWhereUpdated.push(key)
      }
    }

    const stateValuesThatWhereUpdated = []
    for (const key in nextState) {
      if (this.state[key] !== nextState[key]) {
        stateValuesThatWhereUpdated.push(key)
      }
    }

    console.log('EditorControl shouldComponentUpdate', {
      valuesThatWhereUpdated,
      stateValuesThatWhereUpdated,
    })

    return true;
  }

If you add this to the current main branch, you will see hundreds of messages. After the change this drops to 3 or 4 (for each keystroke you type in a field for example).

Checklist

A picture of a cute animal (not mandatory but encouraged)

Clueless

@sempostma sempostma requested a review from a team as a code owner April 23, 2026 15:16
@sempostma sempostma mentioned this pull request Apr 23, 2026
1 task

@yanthomasdev yanthomasdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sempostma, CI is failing because you need to do npm run format in your branch, I would've done that for you but apparently I can't commit to your branch 😓

@yanthomasdev yanthomasdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a few things doing another pass here, let me know if they make sense!

Comment thread packages/decap-cms-core/src/actions/media.ts Outdated
Comment thread packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js Outdated
Comment thread packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js Outdated
sempostma and others added 26 commits May 23, 2026 13:11
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Bumps [axios](https://github.com/axios/axios) from 1.13.5 to 1.15.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.13.5...v1.15.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.15.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 0.1.12 to 0.1.13.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/v.0.1.13/History.md)
- [Commits](pillarjs/path-to-regexp@v0.1.12...v.0.1.13)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-version: 0.1.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](lodash/lodash@4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add linked image support for richtext widget

* chore: revert config.yml

* Apply suggestion from @yanthomasdev

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>

* chore: format

* chore: remove useElement

* fix: formatting

---------

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
* feat: add break element to widget richtext

* fix: format

* chore: revert config

* chore: revert config
* fix: throw proper errors when i18n entries cannot be retrieved

* fix: format i18n code

* fix: typescript errors for older versions

* fix: typing issue

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>

---------

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
…ecaporg#7679)

* fix(frontmatter): improve duplicate frontmatter key error handling

* fix(frontmatter): improve duplicate key warning with file and line info

* fix(frontmatter): made duplicate detection path awarness using indendation

* fix(frontmatter): removed backslash

* feat: use `yaml` diagnostics

---------

Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Co-authored-by: Kavanaugh Latiolais <kav@pelo.tech>
…ecaporg#7766)

* refactor: add email field to User type

Most backends don't need any change because they already populate
this field.

* feat: add config option to enable Signed-off-by in commit messages

Some organizations require Signed-off-by trailers in commit
messages (e.g. for Developer Certificate of Origin). GitHub has a
setting to enable this when authoring changes from the Web UI.
This patch adds a similar setting for DecapCMS.

Closes: decaporg#7730

---------

Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.3.3 to 3.4.0.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.3.3...3.4.0)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…org#7364 (decaporg#7440)

* fix(markdown): extra whitespace before and after pasted content decaporg#7364

* fix(markdown): better solution for extra whitespace before and after pasted content decaporg#7364

---------

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
 - decap-cms@3.12.0
 - decap-cms-app@3.12.0
 - decap-cms-backend-git-gateway@3.6.0
 - decap-cms-backend-gitea@3.4.0
 - decap-cms-backend-github@3.6.0
 - decap-cms-core@3.12.0
 - decap-cms-editor-component-image@3.3.0
 - decap-cms-lib-util@3.5.0
 - decap-cms-ui-default@3.6.0
 - decap-cms-widget-markdown@3.8.0
 - decap-cms-widget-object@3.5.0
 - decap-cms-widget-richtext@3.2.0
 - decap-server@3.7.0
martinjagodic and others added 13 commits May 23, 2026 13:11
 - decap-cms@3.12.1
 - decap-cms-app@3.12.1
…g#6220)

* feat: allow special characters to pass through slugification

* feat: allow special characters to pass through slugification

* chore: combine dev test collections and  rename vars

* chore: remove any

* chore: format

* fix typo in test

* fix: apply Copilot suggestions

* fix: remove unused import

* feat: add new preserve slashes tests

* fix: add missing type and schema updates

* fix: filter empty strings

---------

Co-authored-by: Erez Rokah <erezrokah@users.noreply.github.com>
Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
 - decap-cms@3.12.2
 - decap-cms-app@3.12.2
 - decap-cms-core@3.13.0
 - decap-cms-widget-richtext@3.2.1
…aporg#7443)

* fix: dynamic codemirror mode imports

* style: format code

* fix: await function and add error handling

---------

Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
* fix: adhere to remark's tokenizer rules decaporg#7315

* fix: merge and improve tests

---------

Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
…data (decaporg#7667)

The `invokeEvent` function in registry.js was returning only the data
payload (`_data.entry.get('data')`) instead of returning the complete
entry object. This caused entry metadata fields (slug, path, meta, etc.)
to be lost during event processing, particularly affecting file
collections that rely on slug for file lookups.

When `invokePreSaveEvent` was called during entry persistence:

1. Entry starts with all metadata: slug, path, meta, isModification, etc.
2. `invokeEvent` is called with the full entry object
3. `invokeEvent` returns ONLY the data payload, stripping metadata
4. Calling code replaces the full entry with just the data payload
5. Downstream operations like `entryToRaw` receive incomplete entry
6. `fieldsOrder` method fails trying to match slug to file definitions

This manifested as errors like:
- "No file found for undefined in [collection]"
- "TypeError: can't access property 'toJS', file is undefined"

The bug was introduced in commit 0d7e36b (January 31, 2020) and has
existed for ~5 years, but was only exposed in recent versions (3.2.0+)
when file collection persistence logic started calling `invokePreSaveEvent`
and fully relying on its return value to update the entry draft.

Changed `invokeEvent` to return `_data.entry` (the complete entry object)
instead of `_data.entry.get('data')` (just the data payload).

This preserves all entry metadata through the event handler chain, ensuring
that callers like `invokePreSaveEvent` receive the complete entry object
with all properties intact.

Verified fix resolves file collection publishing issues where:
- Custom widgets modify entry data during save
- File collections with single files rely on slug matching
- Entry metadata must be preserved through preSave event handlers

Fixes #[issue-number-if-exists]

fix(core): test returning full entry when invoking preSave handler

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
…e/EditorControl.js

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
…e/EditorControl.js

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
@sempostma sempostma force-pushed the feature/optimize-editor-performance branch from 7ffee68 to 5a4c351 Compare May 23, 2026 11:12
@sempostma

Copy link
Copy Markdown
Contributor Author

I found a few things doing another pass here, let me know if they make sense!

Nice, I made some changes and rebased the branch.

@sempostma sempostma requested a review from yanthomasdev June 2, 2026 09:07

@yanthomasdev yanthomasdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sempostma, one small comment and I believe it should be good to go. I just need you to update branch here since the branch is protected and I can't do it myself

validateMetaField: memoize(collection => {
return (field, value, t) => {
const state = store.getState();
validateMetaField(state, collection, field, value, t);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be returned as well?

Suggested change
validateMetaField(state, collection, field, value, t);
return validateMetaField(state, collection, field, value, t);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.