├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── setup-workflows.yml │ ├── stalebot.yml │ ├── test-all.yml │ └── update-deps.yml ├── .gitignore ├── .nvmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── karma.conf.js ├── nightwatch.conf.js ├── package.json ├── rollup.config.js ├── samples ├── README.md ├── basic-typescript │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.tsx │ │ ├── index.html │ │ └── index.tsx │ ├── tsconfig.json │ └── webpack.config.js ├── basic │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── component-events │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── CKEditor.jsx │ │ ├── Sidebar.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── component │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── Sidebar.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── editor-url │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── hook-events │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── CKEditor.jsx │ │ ├── Sidebar.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── hook │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── CKEditor.jsx │ │ ├── Sidebar.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── re-order │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── router │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js ├── ssr │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── index.jsx │ │ ├── renderMarkup.jsx │ │ └── server.js │ ├── webpack.config.js │ └── webpack.config.server.js ├── state-lifting │ ├── README.md │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── CKEditor.jsx │ │ ├── TextAreaEditor.jsx │ │ ├── index.html │ │ └── index.jsx │ └── webpack.config.js └── umd │ ├── README.md │ ├── build.js │ ├── package.json │ ├── sandbox.config.json │ └── src │ └── index.html ├── scripts ├── bump.js ├── e2e-runner.js ├── nightwatch-runner.js ├── units-runner.js └── utils.js ├── src ├── CKEditor.tsx ├── events.ts ├── index.ts ├── modules.ts ├── registerEditorEventHandler.ts ├── types.ts ├── useCKEditor.ts └── utils.ts ├── tests ├── e2e │ ├── basic-typescript.js │ ├── basic.js │ ├── component-events.js │ ├── component.js │ ├── hook-events.js │ ├── hook.js │ ├── re-order.js │ ├── ssr.js │ ├── state-lifting.js │ └── umd.js └── unit │ ├── CKEditor.test.tsx │ ├── common.test.tsx │ ├── events.test.ts │ ├── index.ts │ ├── registerEditorEventHandler.test.ts │ ├── useCKEditor.test.tsx │ ├── utils.test.ts │ └── utils.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configurations to normalize the IDE behavior. 2 | # http://editorconfig.org/ 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | tab_width = 4 9 | charset = utf-8 10 | end_of_line = lf 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "eslint-config-ckeditor5", 5 | "plugin:react/recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended" 8 | ], 9 | "settings": { 10 | "react": { 11 | "version": "detect" 12 | } 13 | }, 14 | "rules": { 15 | "@typescript-eslint/explicit-module-boundary-types": "off", 16 | "@typescript-eslint/no-explicit-any": "off", 17 | "no-use-before-define": "off", 18 | "@typescript-eslint/no-use-before-define": [ 19 | "error", 20 | { 21 | "functions": false 22 | } 23 | ], 24 | "@typescript-eslint/consistent-type-imports": "off", 25 | "@typescript-eslint/no-invalid-void-type": "off", 26 | "@typescript-eslint/ban-ts-comment": "off", 27 | "@typescript-eslint/no-var-requires": "off" 28 | }, 29 | "env": { 30 | "browser": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.htaccess eol=lf 4 | *.cgi eol=lf 5 | *.sh eol=lf 6 | 7 | *.css text 8 | *.htm text 9 | *.html text 10 | *.js text 11 | *.json text 12 | *.php text 13 | *.txt text 14 | *.md text 15 | 16 | *.png -text 17 | *.gif -text 18 | *.jpg -text 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Are you reporting a feature request or a bug? 2 | 3 | 10 | 11 | ## Provide detailed reproduction steps (if any) 12 | 13 | 25 | 26 | 1. … 27 | 2. … 28 | 3. … 29 | 30 | ### Expected result 31 | 32 | *What is the expected result of the above steps?* 33 | 34 | ### Actual result 35 | 36 | *What is the actual result of the above steps?* 37 | 38 | ## Other details 39 | 40 | * Browser: … 41 | * OS: … 42 | * Integration version: … 43 | * CKEditor version: … 44 | * Installed CKEditor plugins: … 45 | -------------------------------------------------------------------------------- /.github/workflows/setup-workflows.yml: -------------------------------------------------------------------------------- 1 | name: Setup and update common workflows 2 | 3 | on: 4 | schedule: 5 | - cron: "0 2 * * *" 6 | workflow_dispatch: 7 | inputs: 8 | config: 9 | description: 'Config' 10 | required: false 11 | default: '' 12 | 13 | jobs: 14 | update: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout default branch 19 | # https://github.com/marketplace/actions/checkout 20 | uses: actions/checkout@v2 21 | with: 22 | token: ${{ secrets.GH_WORKFLOWS_TOKEN }} 23 | 24 | - name: Read config 25 | run: | 26 | CONFIG='{}' 27 | if [[ ! -z '${{ github.event.inputs.config }}' ]]; then 28 | CONFIG='${{ github.event.inputs.config }}' 29 | elif [[ -f "./.github/workflows-config.json" ]]; then 30 | CONFIG=$( jq -c .setupWorkflows './.github/workflows-config.json' ) 31 | fi 32 | echo "CONFIG=$CONFIG" >> $GITHUB_ENV 33 | echo "Workflow config: $CONFIG" 34 | 35 | - name: Process config 36 | run: | 37 | AS_PR=$(echo '${{ env.CONFIG }}' | jq -r ".pushAsPullRequest") 38 | if [[ "$AS_PR" == "true" ]]; then 39 | echo "AS_PR=1" >> $GITHUB_ENV 40 | else 41 | echo "AS_PR=0" >> $GITHUB_ENV 42 | fi 43 | BRANCH_SOURCE=$(git rev-parse --abbrev-ref HEAD) 44 | if [[ "$AS_PR" == "true" ]]; then 45 | BRANCH_SOURCE="t/setup-workflows-update_$BRANCH_SOURCE" 46 | fi 47 | echo "BRANCH_SOURCE=$BRANCH_SOURCE" >> $GITHUB_ENV 48 | 49 | - name: Check if update branch already exists 50 | if: env.AS_PR == 1 51 | run: | 52 | if [[ $(git ls-remote --heads | grep ${{ env.BRANCH_SOURCE }} | wc -c) -ne 0 ]]; then 53 | echo "SHOULD_CANCEL=1" >> $GITHUB_ENV 54 | fi 55 | 56 | - name: Cancel build if update branch already exists 57 | if: env.SHOULD_CANCEL == 1 58 | # https://github.com/marketplace/actions/cancel-this-build 59 | uses: andymckay/cancel-action@0.2 60 | 61 | - name: Wait for cancellation 62 | if: env.SHOULD_CANCEL == 1 63 | # https://github.com/marketplace/actions/wait-sleep 64 | uses: jakejarvis/wait-action@master 65 | with: 66 | time: '60s' 67 | 68 | - name: Checkout common workflows repository 69 | # https://github.com/marketplace/actions/checkout 70 | uses: actions/checkout@v2 71 | with: 72 | path: ckeditor4-workflows-common 73 | repository: ckeditor/ckeditor4-workflows-common 74 | ref: master 75 | 76 | - name: Setup workflows directory 77 | run: | 78 | mkdir -p .github/workflows 79 | 80 | - name: Synchronize workflow files 81 | run: | 82 | rsync -a --include='*/' --include='*.yml' --exclude='*' ./ckeditor4-workflows-common/workflows/ ./.github/workflows/ 83 | if [[ $(git status ./.github/workflows/ --porcelain) ]]; then 84 | echo "HAS_CHANGES=1" >> $GITHUB_ENV 85 | fi 86 | 87 | - name: Cleanup common workflows artifacts 88 | run: | 89 | rm -rf ckeditor4-workflows-common 90 | 91 | - name: Checkout PR branch 92 | if: env.HAS_CHANGES == 1 93 | run: | 94 | git checkout -b "t/${{ env.BRANCH_SOURCE }}" 95 | 96 | - name: Add changes 97 | if: env.HAS_CHANGES == 1 98 | run: | 99 | git config --local user.email "${{ secrets.GH_BOT_EMAIL }}" 100 | git config --local user.name "${{ secrets.GH_BOT_USERNAME }}" 101 | git add .github/workflows 102 | git commit -m "Update common workflows." 103 | 104 | - name: Push changes 105 | if: env.HAS_CHANGES == 1 106 | # https://github.com/marketplace/actions/github-push 107 | uses: ad-m/github-push-action@master 108 | with: 109 | github_token: ${{ secrets.GH_WORKFLOWS_TOKEN }} 110 | branch: ${{ env.BRANCH_SOURCE }} 111 | 112 | - name: Create PR 113 | if: env.HAS_CHANGES == 1 && env.AS_PR == 1 114 | # https://github.com/marketplace/actions/github-pull-request-action 115 | uses: repo-sync/pull-request@v2 116 | with: 117 | source_branch: "${{ env.BRANCH_SOURCE }}" 118 | destination_branch: "${{ github.ref }}" 119 | pr_title: "Update 'setup-workflows' workflow" 120 | pr_body: "Update 'setup-workflows' workflow." 121 | github_token: ${{ secrets.GITHUB_TOKEN }} 122 | -------------------------------------------------------------------------------- /.github/workflows/stalebot.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 9 * * *" 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v3 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | stale-issue-message: "It's been a while since we last heard from you. We are marking this issue as stale due to inactivity. Please provide the requested feedback or the issue will be closed after next 7 days." 15 | stale-pr-message: "It's been a while since we last heard from you. We are marking this pull request as stale due to inactivity. Please provide the requested feedback or the pull request will be closed after next 7 days." 16 | close-issue-message: "There was no activity regarding this issue in the last 14 days. We're closing it for now. Still, feel free to provide us with requested feedback so that we can reopen it." 17 | close-pr-message: "There was no activity regarding this pull request in the last 14 days. We're closing it for now. Still, feel free to provide us with requested feedback so that we can reopen it." 18 | stale-issue-label: 'stale' 19 | stale-pr-label: 'stale' 20 | exempt-issue-labels: 'status:confirmed,status:blocked' 21 | exempt-pr-labels: 'status:blocked' 22 | close-issue-label: 'resolution:expired' 23 | close-pr-label: 'pr:frozen ❄' 24 | remove-stale-when-updated: true 25 | days-before-issue-stale: 7 26 | days-before-pr-stale: 14 27 | days-before-close: 7 28 | -------------------------------------------------------------------------------- /.github/workflows/test-all.yml: -------------------------------------------------------------------------------- 1 | name: Test all 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - stable 8 | - major 9 | push: 10 | tags: 11 | - v* 12 | branches: 13 | - master 14 | - stable 15 | - major 16 | 17 | jobs: 18 | # Runs unit tests for all configured React version 19 | test-units: 20 | runs-on: ubuntu-20.04 21 | name: Run unit tests 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v2 25 | 26 | - name: Setup node 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: 14 30 | 31 | - name: Setup Chrome 32 | uses: browser-actions/setup-chrome@latest 33 | with: 34 | chrome-version: stable 35 | 36 | # Set variables required for further steps. 37 | # CHROME_BIN is required by Karma. 38 | # REACT_VERSION is set to "current" for pull request events and "all" for other events. 39 | - name: Set test variables 40 | run: | 41 | export CHROME_BIN=$(which chrome); 42 | if [ -z ${GITHUB_HEAD_REF} ]; then echo "REACT_VERSION=all"; else echo "REACT_VERSION=current"; fi >> $GITHUB_ENV; 43 | 44 | - name: Install dependencies 45 | run: npm install 46 | 47 | # Run tests with the help of Xvfb, since there is no screen output available (required for locally installed browsers). 48 | - name: Run tests 49 | uses: GabrielBB/xvfb-action@v1 50 | env: 51 | CKEDITOR_LICENSE_KEY: ${{ secrets.CKEDITOR_LICENSE_KEY }} 52 | with: 53 | run: npm run test:units -- react ${{ env.REACT_VERSION }} 54 | -------------------------------------------------------------------------------- /.github/workflows/update-deps.yml: -------------------------------------------------------------------------------- 1 | name: Update NPM dependencies 2 | 3 | on: 4 | schedule: 5 | - cron: "0 5 1,15 * *" 6 | workflow_dispatch: 7 | inputs: 8 | config: 9 | description: 'Config' 10 | required: false 11 | default: '' 12 | 13 | jobs: 14 | update: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | deps: [production, dev-only] 20 | 21 | steps: 22 | - name: Setup node 23 | # https://github.com/marketplace/actions/setup-node-js-environment 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: '12' 27 | 28 | - name: Checkout default branch 29 | # https://github.com/marketplace/actions/checkout 30 | uses: actions/checkout@v2 31 | 32 | - name: Read config 33 | run: | 34 | npm i -g npm@7 35 | npm -v 36 | CONFIG='{}' 37 | if [[ ! -z '${{ github.event.inputs.config }}' ]]; then 38 | CONFIG='${{ github.event.inputs.config }}' 39 | elif [[ -f "./.github/workflows-config.json" ]]; then 40 | CONFIG=$( jq -c .updateDeps './.github/workflows-config.json' ) 41 | fi 42 | echo "CONFIG=$CONFIG" >> $GITHUB_ENV 43 | echo "Workflow config: $CONFIG" 44 | 45 | - name: Check env variables 46 | run: | 47 | if [[ -z "${{ secrets.GH_BOT_USERNAME }}" ]]; then 48 | echo "Expected 'GH_BOT_USERNAME' secret variable to be set." 49 | exit 1 50 | fi 51 | if [[ -z "${{ secrets.GH_BOT_EMAIL }}" ]]; then 52 | echo "Expected 'GH_BOT_EMAIL' secret variable to be set." 53 | exit 1 54 | fi 55 | BRANCH_TARGET=$(echo '${{ env.CONFIG }}' | jq -r ".targetBranch") 56 | if [[ -z "$BRANCH_TARGET" || "$BRANCH_TARGET" == "null" ]]; then 57 | # Since 'Fetch changes' step fetches default branch, we just get branch name (which will be default one). 58 | BRANCH_TARGET=$(git rev-parse --abbrev-ref HEAD) 59 | fi 60 | echo "BRANCH_TARGET=$BRANCH_TARGET" >> $GITHUB_ENV 61 | 62 | - name: Check if update branch already exists 63 | run: | 64 | echo "BRANCH_UPDATE=deps-update_${{ env.BRANCH_TARGET }}_${{ matrix.deps }}" >> $GITHUB_ENV 65 | if [[ $(git ls-remote --heads | grep deps-update_${{ env.BRANCH_TARGET }}_${{ matrix.deps }} | wc -c) -ne 0 ]]; then 66 | echo "BRANCH_UPDATE=0" >> $GITHUB_ENV 67 | fi 68 | 69 | - name: Update NPM dependencies 70 | if: env.BRANCH_UPDATE != 0 71 | run: | 72 | npm i 73 | npm install -g npm-check 74 | git checkout -b ${{ env.BRANCH_UPDATE }} 75 | npm-check -y --${{ matrix.deps }} 76 | 77 | - name: Add changes 78 | if: env.BRANCH_UPDATE != 0 79 | run: | 80 | if [[ $(git diff origin/${{ env.BRANCH_TARGET }} | wc -c) -ne 0 ]]; then 81 | git config --local user.email "${{ secrets.GH_BOT_EMAIL }}" 82 | git config --local user.name "${{ secrets.GH_BOT_USERNAME }}" 83 | git add package*.json 84 | git commit -m "Update NPM ${{ matrix.deps }} dependencies." 85 | echo "HAS_CHANGES=1" >> $GITHUB_ENV 86 | fi 87 | 88 | - name: Push changes 89 | if: env.HAS_CHANGES == 1 90 | # https://github.com/marketplace/actions/github-push 91 | uses: ad-m/github-push-action@master 92 | with: 93 | github_token: ${{ secrets.GITHUB_TOKEN }} 94 | branch: ${{ env.BRANCH_UPDATE }} 95 | 96 | - name: Create PR 97 | if: env.HAS_CHANGES == 1 98 | # https://github.com/marketplace/actions/github-pull-request-action 99 | uses: repo-sync/pull-request@v2 100 | with: 101 | source_branch: "${{ env.BRANCH_UPDATE }}" 102 | destination_branch: "${{ env.BRANCH_TARGET }}" 103 | pr_title: "Update NPM ${{ matrix.deps }} dependencies" 104 | pr_body: "Updated NPM ${{ matrix.deps }} dependencies." 105 | github_token: ${{ secrets.GITHUB_TOKEN }} 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .DS_Store 4 | dist/ 5 | samples-out 6 | react-tests/ 7 | *debug.log 8 | .vscode 9 | local.log 10 | public 11 | .tmp* 12 | 13 | # Ignore package-lock.json 14 | # - we don't intent to force specific 3rd party dependency version via `package-lock.json` file 15 | # Such information should be specified in the package.json file. 16 | package-lock.json 17 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | node 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CKEditor 4 WYSIWYG Editor React Integration Changelog 2 | 3 | ⚠️️️ **CKEditor 4 (the open source edition) is no longer maintained.** ⚠️ 4 | 5 | If you would like to keep access to future CKEditor 4 security patches, check the [Extended Support Model](https://ckeditor.com/ckeditor-4-support/), which guarantees **security updates and critical bug fixes until December 2028**. Alternatively, [upgrade to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/updating/ckeditor4/migration-from-ckeditor-4.html). 6 | 7 | ## ckeditor4-react 5.2.1 8 | 9 | Other Changes: 10 | 11 | * Updated default CDN CKEditor 4 dependency to [4.25.1-lts](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4251-lts). 12 | * Updated license headers to 2025. 13 | * Updated readme files to reflect the new CKEditor 4 Extended Support Model end date. 14 | 15 | Please note that this patch release doesn't provide any security fixes. It's a part of our administrative maintenance updates. 16 | 17 | ## ckeditor4-react 5.2.0 18 | 19 | ⚠️️️ CKEditor 4 CDN dependency has been upgraded to the latest secure version. All editor versions below 4.25.0-lts can no longer be considered as secure! ⚠️ 20 | 21 | Other Changes: 22 | 23 | * Updated default CDN CKEditor 4 dependency to [4.25.0-lts](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4250-lts). 24 | 25 | ## ckeditor4-react 5.1.0 26 | 27 | ⚠️️️ CKEditor 4 CDN dependency has been upgraded to the latest secure version. All editor versions below 4.24.0-lts can no longer be considered as secure! ⚠️ 28 | 29 | Other Changes: 30 | 31 | * Updated default CDN CKEditor 4 dependency to [4.24.0-lts](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4240-lts). 32 | 33 | ## ckeditor4-react 5.0.0 34 | 35 | This release introduces a support for the LTS (”Long Term Support”) version of the editor, available under commercial terms (["Extended Support Model"](https://ckeditor.com/ckeditor-4-support/)). 36 | 37 | If you acquired the Extended Support Model for CKEditor 4 LTS, please read [the CKEditor 4 LTS key activation guide.](https://ckeditor.com/docs/ckeditor4/latest/support/licensing/license-key-and-activation.html) 38 | 39 | Other Changes: 40 | 41 | * Updated default CDN CKEditor 4 dependency to [4.23.0-lts](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4230-lts). 42 | 43 | ## ckeditor4-react 4.3.0 44 | 45 | Other Changes: 46 | 47 | * Updated default CDN CKEditor 4 dependency to [4.22.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4220--4221). 48 | 49 | ## ckeditor4-react 4.2.0 50 | 51 | Other Changes: 52 | 53 | * Updated default CDN CKEditor 4 dependency to [4.21.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4210). 54 | 55 | ## ckeditor4-react 4.1.2 56 | 57 | Other Changes: 58 | 59 | * Updated default CDN CKEditor 4 dependency to [4.20.2](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4202). 60 | 61 | ## ckeditor4-react 4.1.1 62 | 63 | Other Changes: 64 | 65 | * Updated default CDN CKEditor 4 dependency to [4.20.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4201). 66 | 67 | ## ckeditor4-react 4.1.0 68 | 69 | Other Changes: 70 | 71 | * Updated default CDN CKEditor 4 dependency to [4.20](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-420). 72 | 73 | ## ckeditor4-react 4.0.0 74 | 75 | **Highlights** 76 | 77 | The v4.0.0 release introduces support for React v18. You can read more about these changes in the [React v18 release notes](https://github.com/facebook/react/blob/main/CHANGELOG.md#1800-march-29-2022). 78 | 79 | Due to significant changes in React v18, the integration with CKEditor 4 is no longer compatible with the previous versions of React. Please note that this version of React also drops support for Internet Explorer 11. 80 | 81 | If you don’t want to lose support for IE11 or you haven't moved to React v18 yet, make sure to use React integration in [version 3](#ckeditor4-react-310). 82 | 83 | See the [browser compatibility table](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_react_current.html#ckeditor-4-react-compatibility) to learn more about supported browsers and React versions. 84 | 85 | BREAKING CHANGES: 86 | 87 | * [#284](https://github.com/ckeditor/ckeditor4-react/issues/284): Add support for React 18 and remove support for older versions of React. 88 | 89 | Other Changes: 90 | 91 | * Updated default CDN CKEditor 4 dependency to [4.19.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4191). 92 | 93 | ## ckeditor4-react 3.1.0 94 | 95 | Other Changes: 96 | 97 | * Updated default CDN CKEditor 4 dependency to [4.19.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4190). 98 | 99 | ## ckeditor4-react 3.0.0 100 | 101 | Other Changes: 102 | 103 | * Updated default CDN CKEditor 4 dependency to [4.18.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4180). 104 | 105 | [Web Spell Checker](https://webspellchecker.com/) ended support for WebSpellChecker Dialog on December 31st, 2021. Therefore, this plugin has been deprecated and removed from the CKEditor 4.18.0 `standard-all` preset. 106 | We strongly encourage everyone to choose one of the other available spellchecking solutions - [Spell Check As You Type (SCAYT)](https://ckeditor.com/cke4/addon/scayt) or [WProofreader](https://ckeditor.com/cke4/addon/wproofreader). 107 | 108 | ## ckeditor4-react 2.1.1 109 | 110 | Other Changes: 111 | 112 | * Updated default CDN CKEditor 4 dependency to [4.17.2](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4172). 113 | * Updated year and company name in the license headers. 114 | 115 | ## ckeditor4-react 2.1.0 116 | 117 | Other Changes: 118 | 119 | * Updated default CDN CKEditor 4 dependency to [4.17.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4171). 120 | 121 | ## ckeditor4-react 2.0.1 122 | 123 | Other Changes: 124 | 125 | * Updated default CDN CKEditor 4 dependency to [4.16.2](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4162). 126 | 127 | ## ckeditor4-react 2.0.0 128 | 129 | New Features: 130 | 131 | * [#228](https://github.com/ckeditor/ckeditor4-react/issues/226): Added support for setting editor's initial data as HTML string. 132 | 133 | ## ckeditor4-react 2.0.0-rc.2 134 | 135 | BREAKING CHANGES: 136 | 137 | * [#226](https://github.com/ckeditor/ckeditor4-react/issues/226): Updated `ckeditor4-integrations-common` dependency to version `1.0.0`. 138 | 139 | ## ckeditor4-react 2.0.0-rc.1 140 | 141 | Other Changes: 142 | 143 | * Added CHANGELOG entries for RC versions. 144 | * Improved project README. 145 | 146 | ## ckeditor4-react 2.0.0-rc.0 147 | 148 | BREAKING CHANGES: 149 | 150 | * [#124](https://github.com/ckeditor/ckeditor4-react/issues/124): Introduced support for React hooks and rewrote the component to use hooks internally. 151 | 152 | New Features: 153 | 154 | * [#159](https://github.com/ckeditor/ckeditor4-react/issues/159): Introduced support for React 17+ versions. 155 | * [#82](https://github.com/ckeditor/ckeditor4-react/issues/82): Introduced TypeScript support. 156 | * [#180](https://github.com/ckeditor/ckeditor4-react/issues/180): Introduced support for consumption of a not bundled package version by providing package in ESM, CJS and UMD formats. 157 | 158 | ## ckeditor4-react 1.4.2 159 | 160 | Other Changes: 161 | 162 | * Updated default CDN CKEditor 4 dependency to [4.16.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4161). 163 | 164 | ## ckeditor4-react 1.4.1 165 | 166 | Fixed Issues: 167 | 168 | * [#114](https://github.com/ckeditor/ckeditor4-react/issues/114), [#127](https://github.com/ckeditor/ckeditor4-react/issues/127): Fixed: The editor uses initial stale data if `data` property changed before editor reaches [`instanceReady` state](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR.html#event-instanceReady). 169 | 170 | ## ckeditor4-react 1.4.0 171 | 172 | Other Changes: 173 | 174 | * Updated default CDN CKEditor 4 dependency to [4.16.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-416). 175 | * Updated [`ckeditor4-integrations-common`](https://www.npmjs.com/package/ckeditor4-integrations-common) package to `0.2.0` version. 176 | * Updated year in license headers. 177 | 178 | ## ckeditor4-react 1.3.0 179 | 180 | New Features: 181 | 182 | * [#125](https://github.com/ckeditor/ckeditor4-react/issues/125): Added `name` property for easier referencing editor instances with [`CKEDITOR.instances`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR.html#property-instances). Thanks to [Julien Castelain](https://github.com/julien)! 183 | * [#129](https://github.com/ckeditor/ckeditor4-react/issues/129): Added `onNamespaceLoaded` property executed when [`CKEDITOR` namespace](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR.html) is loaded, which can be used for its easier customization. 184 | 185 | ## ckeditor4-react 1.2.1 186 | 187 | Other Changes: 188 | 189 | * Updated the default CKEditor 4 CDN dependency to [4.15.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4151). 190 | 191 | ## ckeditor4-react 1.2.0 192 | 193 | Fixed Issues: 194 | 195 | * [#94](https://github.com/ckeditor/ckeditor4-react/issues/94): Fixed: The [`editor-incorrect-element`](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_errors.html#editor-incorrect-element) error is thrown due to `null` element reference when editor instance is destroyed before initialization completes. Thanks to [Christoph Dörfel](https://github.com/Garbanas)! 196 | 197 | Other Changes: 198 | 199 | * Updated the default CKEditor 4 CDN dependency to [4.15.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-415). 200 | 201 | ## ckeditor4-react 1.1.1 202 | 203 | Other Changes: 204 | 205 | * Updated the default CKEditor 4 CDN dependency to [4.14.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4141). 206 | 207 | ## ckeditor4-react 1.1.0 208 | 209 | Fixed Issues: 210 | 211 | * [#57](https://github.com/ckeditor/ckeditor4-react/issues/57): Fixed: The CKEditor 4 WYSIWYG editor React integration gives an [`editor-element-conflict` error](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_errors.html#editor-element-conflict). 212 | 213 | Other Changes: 214 | 215 | * Updated the default CKEditor 4 CDN dependency to [4.14.0](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-414). 216 | 217 | ## ckeditor4-react 1.0.1 218 | 219 | Other Changes: 220 | 221 | * Updated the default CKEditor 4 CDN dependency to [4.13.1](https://github.com/ckeditor/ckeditor4/blob/master/CHANGES.md#ckeditor-4131). 222 | 223 | ## ckeditor4-react 1.0.0 224 | 225 | New Features: 226 | 227 | * [#15](https://github.com/ckeditor/ckeditor4-react/issues/15): Introduced support for Server Side Rendering. 228 | 229 | Fixed Issues: 230 | 231 | * [#46](https://github.com/ckeditor/ckeditor4-react/issues/46): Fixed: The React integration tries to destroy a non-existent editor instance in some cases. Thanks to [Oleg Kachmar](https://github.com/prokach)! 232 | * [#44](https://github.com/ckeditor/ckeditor4-react/issues/44): Fixed: An error thrown when changing routes quickly. 233 | * [#49](https://github.com/ckeditor/ckeditor4-react/issues/49): Fixed: A "Cannot read property 'getEditor' of null" error thrown when the component is unmounted. 234 | * [#56](https://github.com/ckeditor/ckeditor4-react/issues/56): Fixed: CKEditor crashes when unmounting with a "Cannot read property 'destroy' of null" error. 235 | 236 | Other Changes: 237 | 238 | * Updated the default CKEditor 4 CDN dependency to [`4.13.0`](https://github.com/ckeditor/ckeditor4-react/commit/7b34d2c4f896ced08e66359faca28194ed7e8ef4). 239 | 240 | ## ckeditor4-react 1.0.0-beta.2 241 | 242 | New Features: 243 | 244 | * [#47](https://github.com/ckeditor/ckeditor4-react/issues/47): Exposed the `CKEDITOR` namespace before loading the editor instance. Thanks to [Nick Rattermann](https://github.com/nratter)! 245 | * [#48](https://github.com/ckeditor/ckeditor4-react/pull/48): Added `CKEDITOR.displayName` for easy debugging and testing. Thanks to [Florent Berthelot](https://github.com/FBerthelot)! 246 | 247 | Other Changes: 248 | 249 | * Updated the default CKEditor 4 CDN dependency to [`4.12.1`](https://github.com/ckeditor/ckeditor4-react/commit/e72c2fb2d8e107419fe209c436c909915237a109). 250 | 251 | ## ckeditor4-react 1.0.0-beta 252 | 253 | Other Changes: 254 | 255 | * Updated the `LICENSE.md` file with all development and production dependencies. 256 | 257 | ## ckeditor4-react 0.1.1 258 | 259 | Other Changes: 260 | 261 | * Updated all CKEditor 4 dependencies to the `4.11.4` version. 262 | * `README.md` file improvements. 263 | 264 | ## ckeditor4-react 0.1.0 265 | 266 | The first beta release of the CKEditor 4 WYSIWYG Editor React Integration. 267 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ### Install dependencies 4 | 5 | After cloning this repository, install necessary dependencies: 6 | 7 | ``` 8 | npm install 9 | ``` 10 | 11 | ### Development 12 | 13 | ``` 14 | npm run develop 15 | ``` 16 | 17 | After running this command unit tests will start in watch mode and you are ready to develop. Please note that source code will be transpiled with `babel` so there is no type checking with TypeScript for this command. In order to validate types with TS run `npm run types:check` or run a full build (see below). 18 | 19 | By default Karma will run tests on Chrome so make sure that its binary is available on your system. If you have Chrome installed but Karma cannot find its binary for some reason, you might need to set `CHROME_BIN` environment variable, e.g. in your `.bashrc` file: `export CHROME_BIN=path_to_chrome_bin`. 20 | 21 | ### Build 22 | 23 | #### Watch mode 24 | 25 | Run the following command in order to start builds in watch mode: 26 | 27 | ``` 28 | npm start 29 | ``` 30 | 31 | This command emits bundles in following formats: `umd`, `esm`, `cjs`. Type declarations are not be emitted. 32 | 33 | #### Run build once 34 | 35 | Run `npm run build` command to run build once. TypeScript declarations are emitted. 36 | 37 | ### Tests 38 | 39 | #### Quick feedback 40 | 41 | As noted above, run `npm run develop` to quickly run a full suite of unit tests on currently installed React version. 42 | 43 | To run more a complete test script, just run the following command: 44 | 45 | ``` 46 | npm test 47 | ``` 48 | 49 | This command makes a single run of linter, type checker and unit tests. 50 | 51 | #### Full suite of unit tests 52 | 53 | In order to run unit tests on all supported React versions: 54 | 55 | ``` 56 | npm run test:units:all 57 | ``` 58 | 59 | #### Full suite of E2E tests 60 | 61 | E2E tests are to ensure that library _build files_ can be used with minimal setup on the consumer end and that they work on all supported browsers. E2E tests will run on examples inside `samples` directory. Tests will be run for each supported React version. 62 | 63 | All tests will run on BrowserStack, so make sure that environment variables `BROWSER_STACK_USERNAME` and `BROWSER_STACK_ACCESS_KEY` are set beforehand (values must be kept secret). In addition set `BROWSER_STACK_BROWSER` variable to one of the following values: `chrome`, `safari`, `ie`, `firefox`, `edge`. 64 | 65 | E2E tests are meant to run in CI environment but it's possible to run them locally as well. For example: 66 | 67 | ``` 68 | BROWSER_STACK_USERNAME=name BROWSER_STACK_ACCESS_KEY=key BROWSER_STACK_BROWSER=safari npm run test:e2e:all 69 | ``` 70 | 71 | Or for a single sample: 72 | 73 | ``` 74 | BROWSER_STACK_USERNAME=name BROWSER_STACK_ACCESS_KEY=key BROWSER_STACK_BROWSER=safari npm run test:e2e:all -- --sample basic 75 | ``` 76 | 77 | ### Samples 78 | 79 | Before running any sample locally, make sure to expose root package as a link and build the library: 80 | 81 | ``` 82 | npm link && npm run build 83 | ``` 84 | 85 | It's enough to `npm link` the root package once. Run build on every change in root `src` folder. 86 | 87 | Example apps are located in `samples` directory. Each sample is a self-contained application that has mandatory `start` and `build` scripts. In order to run a sample go thorugh the following steps: 88 | 89 | 1. `cd` into sample, e.g. `cd samples/basic` 90 | 2. Run `npm install` 91 | 3. Link root package: `npm link ckeditor4-react` 92 | 4. Start example: `npm start` 93 | 5. Navigate to `localhost:8080` 94 | 95 | It's important to re-run `npm link ckeditor4-react` in a sample folder anytime `npm install` operation was performed in that sample! 96 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | ## Software License Agreement for CKEditor 4 LTS React component (5.0 and above) 5 | 6 | **CKEditor 4 WYSIWYG editor component for React** – https://github.com/ckeditor/ckeditor4-react
7 | Copyright (c) 2003-2025, [CKSource](http://cksource.com) Holding sp. z o.o. All rights reserved. 8 | 9 | CKEditor 4 LTS ("Long Term Support") is available under exclusive terms of the [Extended Support Model](https://ckeditor.com/ckeditor-4-support/). [Contact us](https://ckeditor.com/contact/) to obtain a commercial license. 10 | 11 | ## Software License Agreement for CKEditor 4 React component 4.3.0 and below 12 | 13 | **CKEditor 4 WYSIWYG editor component for React** – https://github.com/ckeditor/ckeditor4-react
14 | Copyright (c) 2003-2025, [CKSource](http://cksource.com) Holding sp. z o.o. All rights reserved. 15 | 16 | Licensed under the terms of any of the following licenses at your 17 | choice: 18 | 19 | - GNU General Public License Version 2 or later (the "GPL") 20 | http://www.gnu.org/licenses/gpl.html 21 | 22 | - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 23 | http://www.gnu.org/licenses/lgpl.html 24 | 25 | - Mozilla Public License Version 1.1 or later (the "MPL") 26 | http://www.mozilla.org/MPL/MPL-1.1.html 27 | 28 | Sources of Intellectual Property Included in CKEditor 29 | ----------------------------------------------------- 30 | 31 | Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission. 32 | 33 | The following libraries are included in CKEditor 4 WYSIWYG editor component for React under the [MIT license](https://opensource.org/licenses/MIT): 34 | - load-script 35 | - prop-types (c) 2013-present, Facebook, Inc. 36 | 37 | The following libraries are included in CKEditor 4 WYSIWYG editor component for React under the [0BSD license](https://opensource.org/licenses/0BSD): 38 | - tslib (c) Microsoft Corporation 39 | 40 | Trademarks 41 | ---------- 42 | 43 | **CKEditor** is a trademark of [CKSource](http://cksource.com) Holding sp. z o.o. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CKEditor 4 WYSIWYG editor component for React [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Check%20out%20CKEditor%204%20React%20integration&url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fckeditor4-react) 2 | 3 | [![npm version](https://badge.fury.io/js/ckeditor4-react.svg)](https://www.npmjs.com/package/ckeditor4-react) 4 | [![GitHub tag](https://img.shields.io/github/tag/ckeditor/ckeditor4-react.svg)](https://github.com/ckeditor/ckeditor4-react) 5 | 6 | ![Build Status](https://github.com/ckeditor/ckeditor4-react/actions/workflows/test-all.yml/badge.svg) 7 | 8 | [![Join newsletter](https://img.shields.io/badge/join-newsletter-00cc99.svg)](http://eepurl.com/c3zRPr) 9 | [![Follow Twitter](https://img.shields.io/badge/follow-twitter-00cc99.svg)](https://twitter.com/ckeditor) 10 | 11 | ## ⚠️ CKEditor 4: End of Life and Extended Support Model until Dec 2028 12 | 13 | CKEditor 4 was launched in 2012 and reached its End of Life (EOL) on June 30, 2023. 14 | 15 | A special edition, **[CKEditor 4 LTS](https://ckeditor.com/ckeditor-4-support/)** ("Long Term Support"), is available under commercial terms (["Extended Support Model"](https://ckeditor.com/ckeditor-4-support/)) for anyone looking to **extend the coverage of security updates and critical bug fixes**. 16 | 17 | With CKEditor 4 LTS, security updates and critical bug fixes are guaranteed until December 2028. 18 | 19 | ## About this repository 20 | 21 | ### Master branch = CKEditor 4 LTS React Component 22 | 23 | After June 30, 2023 the `master` version of the [LICENSE.md](https://github.com/ckeditor/ckeditor4/blob/master/LICENSE.md) file changed to reflect the license of CKEditor 4 LTS available under the Extended Support Model. 24 | 25 | This repository now contains the source code of CKEditor 4 LTS React Component that is protected by copyright law. 26 | 27 | ### Getting CKEditor 4 (Open Source) 28 | 29 | You may continue using CKEditor React Component 4.3.0 and below under the open source license terms. Please note, however, that the open source version no longer comes with any security updates, so your application will be at risk. 30 | 31 | In order to download the open source version of CKEditor 4 React Component, use ****tags 4.3.0 and below****. CKEditor React Component 4.3.0 was the last version available under the open source license terms. 32 | 33 | ## About this package 34 | 35 | Official [CKEditor 4](https://ckeditor.com/ckeditor-4/) WYSIWYG editor component for React. 36 | 37 | We are looking forward to your feedback! You can report any issues, ideas or feature requests on the [integration issues page](https://github.com/ckeditor/ckeditor4-react/issues/new). 38 | 39 | ![CKEditor 4 screenshot](https://c.cksource.com/a/1/img/npm/ckeditor4.png) 40 | 41 | ## Usage 42 | 43 | ```jsx 44 | import React from 'react'; 45 | import { CKEditor } from 'ckeditor4-react'; 46 | 47 | function App() { 48 | return ; 49 | } 50 | 51 | export default App; 52 | ``` 53 | 54 | ## Documentation and examples 55 | 56 | See the [CKEditor 4 WYSIWYG Editor React Integration](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_react_current.html) article in the [CKEditor 4 documentation](https://ckeditor.com/docs/ckeditor4/latest). 57 | 58 | You can also check out [CKEditor 4 WYSIWYG Editor React Integration example](https://ckeditor.com/docs/ckeditor4/latest/examples/react.html) in [CKEditor 4 Examples](https://ckeditor.com/docs/ckeditor4/latest/examples/). 59 | 60 | For even more examples, check out ready-to-fork samples inside [samples](samples) directory. Each sample is a self-contained app that can be [forked via GitHub](https://docs.github.com/en/github/getting-started-with-github/splitting-a-subfolder-out-into-a-new-repository) or via services such as [CodeSandbox](https://codesandbox.io/). For instance, in order to clone `basic` sample, use [this link](https://githubbox.com/ckeditor/ckeditor4-react/tree/master/samples/basic). 61 | 62 | ## React support 63 | 64 | The CKEditor 4 React integration was tested with React 18. 65 | 66 | ## TypeScript support 67 | 68 | TypeScript 3.5+ is supported. 69 | 70 | ## Browser support 71 | 72 | The CKEditor 4 React integration works with all the [supported browsers](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_browsers.html#officially-supported-browsers) except for Internet Explorer. 73 | 74 | Previous versions of `ckeditor4-react` also support Internet Explorer 11 (requires additional polyfill for `Promise`). 75 | 76 | ## Contributing 77 | 78 | See [CONTRIBUTING.md](CONTRIBUTING.md). 79 | 80 | ## License 81 | 82 | Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. 83 | 84 | For full details about the license, please check the `LICENSE.md` file. 85 | 86 | ### CKEditor 4 React Component 4.3.0 and below for CKEditor 4 Open Source 87 | 88 | Licensed under the terms of any of the following licenses at your 89 | choice: 90 | 91 | * [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html), 92 | * [GNU Lesser General Public License Version 2.1 or later](http://www.gnu.org/licenses/lgpl.html), 93 | * [Mozilla Public License Version 1.1 or later (the "MPL")](http://www.mozilla.org/MPL/MPL-1.1.html). 94 | 95 | ### CKEditor 4 React Component 5.0 and above for CKEditor 4 LTS ("Long Term Support") 96 | 97 | CKEditor 4 LTS React Component (starting from version 5.0) is available under a commercial license only. 98 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | 4 | const babel = require( '@rollup/plugin-babel' ).babel; 5 | const nodeResolve = require( '@rollup/plugin-node-resolve' ).nodeResolve; 6 | const replace = require( '@rollup/plugin-replace' ); 7 | const commonJs = require( '@rollup/plugin-commonjs' ); 8 | const polyfillNode = require( 'rollup-plugin-polyfill-node' ); 9 | const progress = require( 'rollup-plugin-progress' ); 10 | 11 | module.exports = function( config ) { 12 | config.set( { 13 | browsers: [ 'Chrome' ], 14 | 15 | frameworks: [ 'jasmine' ], 16 | 17 | files: [ 18 | // Uses single point of entry for improved build performance. 19 | { pattern: 'tests/unit/index.ts', watched: false } 20 | ], 21 | 22 | client: { 23 | jasmine: { 24 | random: false 25 | }, 26 | args: [ 27 | process.env.CKEDITOR_LICENSE_KEY 28 | ] 29 | }, 30 | 31 | preprocessors: { 32 | 'tests/unit/index.ts': [ 'rollup' ] 33 | }, 34 | 35 | reporters: [ 'mocha' ], 36 | 37 | rollupPreprocessor: { 38 | output: { 39 | format: 'iife', 40 | name: 'CKEditor4React' 41 | }, 42 | watch: { 43 | skipWrite: true 44 | }, 45 | plugins: [ 46 | !config.silentBuildLogs && progress(), 47 | babel( { 48 | babelHelpers: 'bundled', 49 | presets: [ 50 | '@babel/preset-env', 51 | '@babel/preset-typescript', 52 | '@babel/preset-react' 53 | ], 54 | extensions: [ '.ts', '.tsx', '.js' ], 55 | exclude: 'node_modules/**' 56 | } ), 57 | nodeResolve( { 58 | preferBuiltins: false, 59 | extensions: [ '.ts', '.tsx', '.js' ] 60 | } ), 61 | commonJs(), 62 | polyfillNode(), 63 | replace( { 64 | preventAssignment: true, 65 | values: { 66 | 'process.env.REQUESTED_REACT_VERSION': `"${ 67 | process.env.REQUESTED_REACT_VERSION || '' 68 | }"`, 69 | 'process.env.NODE_ENV': '"test"' 70 | } 71 | } ) 72 | ].filter( Boolean ), 73 | onwarn( warning, rollupWarn ) { 74 | if ( 75 | // Reduces noise for circular deps. 76 | // https://github.com/rollup/rollup/issues/2271 77 | warning.code !== 'CIRCULAR_DEPENDENCY' && 78 | // Prevents namespace warning when bundling RTL. 79 | // https://github.com/testing-library/react-testing-library/issues/790 80 | warning.code !== 'NAMESPACE_CONFLICT' 81 | ) { 82 | rollupWarn( warning ); 83 | } 84 | } 85 | } 86 | } ); 87 | }; 88 | -------------------------------------------------------------------------------- /nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | /** 4 | * !!! Important !!! 5 | * 6 | * Nightwatch for BrowserStack is supposed to be run via `scripts/nightwatch-runner.js`. 7 | */ 8 | 9 | // The following environment variables must be set (e.g. in .bashrc or via CLI) before running `nightwatch-runner`. 10 | const bsUser = process.env.BROWSER_STACK_USERNAME; 11 | const bsKey = process.env.BROWSER_STACK_ACCESS_KEY; 12 | const bsBrowser = process.env.BROWSER_STACK_BROWSER; 13 | 14 | // The following environment variables should be set before running `nightwatch-runner` but are optional. 15 | const bsBuildName = process.env.BROWSER_STACK_BUILD_NAME; 16 | 17 | // The following variables will be set via `nightwatch-runner` script. 18 | // It's important that `localIdentifier` passed to `browserstack-local` and to Nightwatch config match. 19 | const bsLocalIdentifier = process.env.BROWSER_STACK_LOCAL_IDENTIFIER; 20 | 21 | const browsers = { 22 | chrome: { 23 | os: 'Windows', 24 | os_version: '10' 25 | }, 26 | edge: { 27 | os: 'Windows', 28 | os_version: '10' 29 | }, 30 | firefox: { 31 | os: 'Windows', 32 | os_version: '10' 33 | }, 34 | ie: { 35 | os: 'Windows', 36 | os_version: '10' 37 | }, 38 | safari: { 39 | os: 'OS X', 40 | os_version: 'Catalina' 41 | } 42 | }; 43 | 44 | const nightwatchConfig = { 45 | selenium: { 46 | start_process: false, 47 | host: 'hub-cloud.browserstack.com', 48 | port: 443 49 | }, 50 | 51 | output_folder: '.tmp-e2e-output', 52 | 53 | test_settings: { 54 | default: { 55 | desiredCapabilities: { 56 | build: bsBuildName, 57 | 'browserstack.user': bsUser, 58 | 'browserstack.key': bsKey, 59 | 'browserstack.debug': true, 60 | 'browserstack.local': true, 61 | 'browserstack.localIdentifier': bsLocalIdentifier, 62 | browser: bsBrowser, 63 | version: 'latest', 64 | ...browsers[ bsBrowser ] 65 | } 66 | } 67 | } 68 | }; 69 | 70 | for ( const i in nightwatchConfig.test_settings ) { 71 | const config = nightwatchConfig.test_settings[ i ]; 72 | config.selenium_host = nightwatchConfig.selenium.host; 73 | config.selenium_port = nightwatchConfig.selenium.port; 74 | } 75 | 76 | module.exports = nightwatchConfig; 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ckeditor4-react", 3 | "version": "5.2.1", 4 | "description": "Official React component for CKEditor 4 – the best browser-based rich text editor.", 5 | "license": "SEE LICENSE IN LICENSE.md", 6 | "main": "dist/index.cjs.js", 7 | "module": "dist/index.esm.js", 8 | "types": "dist/index.d.ts", 9 | "files": [ 10 | "dist/" 11 | ], 12 | "scripts": { 13 | "build": "rollup -c && npm run types:emit", 14 | "bump": "node ./scripts/bump.js", 15 | "develop": "karma start", 16 | "lint": "eslint --ext .js,.ts,.tsx src tests scripts", 17 | "postversion": "git rm -r --cached dist/ && git commit -m \"Clean after release [ci skip]\" && git push origin && git push origin --tags", 18 | "preversion": "npm test", 19 | "start": "rollup -c -w", 20 | "test:e2e:all": "npm run test:e2e -- --react all", 21 | "test:e2e": "node ./scripts/e2e-runner.js", 22 | "test:units:all": "npm run test:units -- --react all", 23 | "test:units": "node ./scripts/units-runner.js", 24 | "test": "npm run lint && npm run types:check && npm run test:units -- --silent-build-logs=true", 25 | "types:check": "tsc --noEmit", 26 | "types:emit": "tsc src/index.ts --jsx react --emitDeclarationOnly --declaration --declarationDir dist", 27 | "version": "npm run build && git add -f dist/" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/ckeditor/ckeditor4-react.git" 32 | }, 33 | "keywords": [ 34 | "wysiwyg", 35 | "rich text", 36 | "editor", 37 | "html", 38 | "contentEditable", 39 | "editing", 40 | "react", 41 | "react-component", 42 | "react-hooks", 43 | "react hooks", 44 | "ckeditor", 45 | "ckeditor4", 46 | "ckeditor 4" 47 | ], 48 | "author": "CKSource (http://cksource.com/)", 49 | "bugs": { 50 | "url": "https://github.com/ckeditor/ckeditor4-react/issues" 51 | }, 52 | "homepage": "https://github.com/ckeditor/ckeditor4-react#readme", 53 | "peerDependencies": { 54 | "ckeditor4": "^4.25.1", 55 | "react": "^18" 56 | }, 57 | "devDependencies": { 58 | "@babel/core": "^7.20.12", 59 | "@babel/preset-env": "^7.20.2", 60 | "@babel/preset-react": "^7.18.6", 61 | "@babel/preset-typescript": "^7.18.6", 62 | "@rollup/plugin-babel": "^5.3.1", 63 | "@rollup/plugin-commonjs": "^22.0.2", 64 | "@rollup/plugin-node-resolve": "^13.3.0", 65 | "@rollup/plugin-replace": "^4.0.0", 66 | "@rollup/plugin-typescript": "^8.5.0", 67 | "@testing-library/jasmine-dom": "^1.3.3", 68 | "@testing-library/react": "^13.4.0", 69 | "@types/jasmine": "^4.3.1", 70 | "@types/prop-types": "^15.7.5", 71 | "@types/react": "^18.0.28", 72 | "@typescript-eslint/eslint-plugin": "^5.51.0", 73 | "@typescript-eslint/parser": "^5.52.0", 74 | "browserstack-local": "^1.5.1", 75 | "chalk": "^4.1.2", 76 | "ckeditor4": "^4.25.1", 77 | "eslint": "^8.34.0", 78 | "eslint-config-ckeditor5": "^4.1.1", 79 | "eslint-plugin-react": "^7.32.2", 80 | "eslint-plugin-react-hooks": "^4.6.0", 81 | "jasmine": "^4.5.0", 82 | "karma": "^6.4.1", 83 | "karma-babel-preprocessor": "^8.0.2", 84 | "karma-chrome-launcher": "^3.1.1", 85 | "karma-jasmine": "^5.1.0", 86 | "karma-mocha-reporter": "^2.2.5", 87 | "karma-rollup-preprocessor": "^7.0.8", 88 | "minimist": "^1.2.8", 89 | "nightwatch": "2.0.10", 90 | "react": "^18.2.0", 91 | "react-dom": "^18.2.0", 92 | "rollup": "^2.79.1", 93 | "rollup-plugin-cleanup": "^3.2.1", 94 | "rollup-plugin-polyfill-node": "^0.10.2", 95 | "rollup-plugin-progress": "^1.1.2", 96 | "rollup-plugin-terser": "^7.0.2", 97 | "semver": "^7.3.8", 98 | "shelljs": "^0.8.5", 99 | "tree-kill": "^1.2.2", 100 | "tslib": "^2.5.0", 101 | "typescript": "^4.9.5" 102 | }, 103 | "dependencies": { 104 | "ckeditor4-integrations-common": "^1.0.0", 105 | "prop-types": "^15.8.1" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import commonJs from '@rollup/plugin-commonjs'; 4 | import typescript from '@rollup/plugin-typescript'; 5 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 6 | import replace from '@rollup/plugin-replace'; 7 | import { terser } from 'rollup-plugin-terser'; 8 | import cleanup from 'rollup-plugin-cleanup'; 9 | import pkg from './package.json'; 10 | 11 | const input = 'src/index.ts'; 12 | const external = Object.keys( pkg.peerDependencies || {} ); 13 | const banner = `/** 14 | * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. 15 | * For licensing, see LICENSE.md. 16 | */`; 17 | 18 | export default [ 19 | // Creates `umd` build that can be directly consumed via 26 | 27 | `; 28 | } 29 | 30 | export default renderMarkup; 31 | -------------------------------------------------------------------------------- /samples/ssr/src/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import path from 'path'; 4 | import express from 'express'; 5 | import renderMarkup from './renderMarkup'; 6 | 7 | const PORT = process.env.PORT || 8080; 8 | const app = express(); 9 | 10 | app.get( '/', ( _, res ) => { 11 | res.send( renderMarkup() ); 12 | } ); 13 | 14 | app.use( express.static( path.resolve( __dirname, '../public' ) ) ); 15 | 16 | app.listen( PORT, () => { 17 | console.log( `Server is listening on port ${ PORT }` ); 18 | } ); 19 | -------------------------------------------------------------------------------- /samples/ssr/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const path = require( 'path' ); 4 | 5 | module.exports = { 6 | entry: './src/index.jsx', 7 | mode: 'development', 8 | devtool: 'source-map', 9 | output: { 10 | path: path.resolve( __dirname, './public' ), 11 | filename: 'bundle.js' 12 | }, 13 | resolve: { 14 | extensions: [ '.js', '.jsx' ], 15 | alias: { 16 | react$: path.resolve( __dirname, './node_modules/react' ), 17 | 'react-dom$': path.resolve( __dirname, './node_modules/react-dom' ) 18 | } 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.jsx?$/, 24 | exclude: /node_modules/, 25 | use: { 26 | loader: 'babel-loader', 27 | options: { 28 | presets: [ '@babel/preset-react', '@babel/preset-env' ] 29 | } 30 | } 31 | } 32 | ] 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /samples/ssr/webpack.config.server.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const path = require( 'path' ); 4 | const config = require( './webpack.config' ); 5 | 6 | module.exports = { 7 | ...config, 8 | entry: './src/server.js', 9 | output: { 10 | path: path.resolve( __dirname, './dist' ), 11 | filename: 'server.js' 12 | }, 13 | target: [ 'node' ] 14 | }; 15 | -------------------------------------------------------------------------------- /samples/state-lifting/README.md: -------------------------------------------------------------------------------- 1 | # State lifting 2 | 3 | Editor's state is lifted higher up React tree to establish connection between editor and a custom `textarea`. 4 | 5 | This sample was known as two-way data binding in v1. 6 | 7 | Demo is available [here](https://githubbox.com/ckeditor/ckeditor4-react/tree/stable/samples/state-lifting). 8 | -------------------------------------------------------------------------------- /samples/state-lifting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "webpack --mode production", 5 | "start": "webpack serve" 6 | }, 7 | "browserslist": [ 8 | ">0.2%", 9 | "not dead", 10 | "not op_mini all" 11 | ], 12 | "dependencies": { 13 | "ckeditor4-react": "latest", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.18.6", 19 | "@babel/preset-env": "^7.18.6", 20 | "@babel/preset-react": "^7.18.6", 21 | "babel-loader": "^8.2.5", 22 | "html-webpack-plugin": "^5.5.0", 23 | "webpack": "^5.73.0", 24 | "webpack-cli": "^4.10.0", 25 | "webpack-dev-server": "^4.9.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/state-lifting/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CKEditorEventAction } from 'ckeditor4-react'; 3 | import TextAreaEditor from './TextAreaEditor'; 4 | import CKEditor from './CKEditor'; 5 | 6 | const { useReducer } = React; 7 | 8 | function App() { 9 | const [ state, dispatch ] = useReducer( reducer, { 10 | data: '', 11 | currentEditor: undefined 12 | } ); 13 | 14 | return ( 15 |
16 |
17 |
18 | 19 |
20 |
21 | { /* Remember to add the license key to the CKEditor 4 configuration: 22 | https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-licenseKey */ } 23 | 24 |
25 |
26 |
27 |
28 |

{`Current editor: ${ state.currentEditor || '' }`}

29 |
33 |
34 |
35 |
{`Running React v${ React.version }`}
36 |
37 | ); 38 | } 39 | 40 | function reducer( state, action ) { 41 | switch ( action.type ) { 42 | case 'textareaBlur': { 43 | return { 44 | ...state, 45 | currentEditor: undefined 46 | }; 47 | } 48 | case 'textareaData': { 49 | return { 50 | currentEditor: 'textarea', 51 | data: action.payload 52 | }; 53 | } 54 | case 'textareaFocus': { 55 | return { 56 | ...state, 57 | currentEditor: 'textarea' 58 | }; 59 | } 60 | case CKEditorEventAction.blur: { 61 | return { 62 | ...state, 63 | currentEditor: 64 | state.currentEditor !== 'textarea' ? 65 | undefined : 66 | state.currentEditor 67 | }; 68 | } 69 | case CKEditorEventAction.change: { 70 | return { 71 | ...state, 72 | data: action.payload.editor.getData() 73 | }; 74 | } 75 | case CKEditorEventAction.focus: { 76 | return { 77 | ...state, 78 | currentEditor: 'CKEditor' 79 | }; 80 | } 81 | default: { 82 | return state; 83 | } 84 | } 85 | } 86 | 87 | export default App; 88 | -------------------------------------------------------------------------------- /samples/state-lifting/src/CKEditor.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | 3 | import React from 'react'; 4 | import { useCKEditor } from 'ckeditor4-react'; 5 | 6 | const { useEffect, useMemo, useState } = React; 7 | 8 | function CKEditor( { dispatch, state } ) { 9 | const [ element, setElement ] = useState(); 10 | 11 | const { editor } = useCKEditor( { 12 | element, 13 | debug: true, 14 | dispatchEvent: dispatch, 15 | subscribeTo: [ 'blur', 'focus', 'change' ] 16 | } ); 17 | 18 | /** 19 | * Invoking `editor.setData` too often might freeze the browser. 20 | */ 21 | const setEditorData = useMemo( () => { 22 | if ( editor ) { 23 | /* eslint-disable-next-line */ 24 | return new CKEDITOR.tools.buffers.throttle( 500, data => { 25 | if ( editor ) { 26 | editor.setData( data ); 27 | } 28 | } ).input; 29 | } 30 | }, [ editor ] ); 31 | 32 | /** 33 | * Sets editor data if it comes from a different source. 34 | */ 35 | useEffect( () => { 36 | if ( state.currentEditor === 'textarea' && setEditorData ) { 37 | setEditorData( state.data ); 38 | } 39 | }, [ setEditorData, state ] ); 40 | 41 | return
; 42 | } 43 | 44 | export default CKEditor; 45 | -------------------------------------------------------------------------------- /samples/state-lifting/src/TextAreaEditor.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | 3 | import React from 'react'; 4 | 5 | const { useEffect, useState } = React; 6 | 7 | function TextAreaEditor( { dispatch, state } ) { 8 | const [ value, setValue ] = useState(); 9 | 10 | const handleTextAreaChange = evt => { 11 | const value = evt.currentTarget.value; 12 | setValue( value ); 13 | dispatch( { type: 'textareaData', payload: value } ); 14 | }; 15 | 16 | const handleBlur = () => { 17 | dispatch( { type: 'textareaBlur' } ); 18 | }; 19 | 20 | const handleFocus = () => { 21 | dispatch( { type: 'textareaFocus' } ); 22 | }; 23 | 24 | /** 25 | * Sets text area value if it comes from a different source. 26 | */ 27 | useEffect( () => { 28 | if ( state.currentEditor === 'CKEditor' ) { 29 | setValue( state.data ); 30 | } 31 | }, [ state ] ); 32 | 33 | return ( 34 |