Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to integrate CKEditor5 from source in create-react-app@3+ #152

Closed
henok-tesfaye opened this issue May 21, 2020 · 13 comments
Closed

How to integrate CKEditor5 from source in create-react-app@3+ #152

henok-tesfaye opened this issue May 21, 2020 · 13 comments
Labels
pending:feedback This issue is blocked by necessary feedback. squad:integrations type:question

Comments

@henok-tesfaye
Copy link

I checked this doc, https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/react.html#integrating-ckeditor-5-built-from-source, and it is for create-react-app@2+, how about for@ 3+, without ejecting the webpack, like using https://www.npmjs.com/package/react-app-rewired.

@FilipTokarski
Copy link
Member

Hi, integrating CKEditor 5 with React from source is the same in create-react-app@3 as in 2, please follow integration tutorial. I just checked it and it works fine for me.

As it comes to using react-app-rewired, @ma2ciek can you take a look at it?

@FilipTokarski FilipTokarski added type:question pending:feedback This issue is blocked by necessary feedback. labels May 22, 2020
@ma2ciek
Copy link
Contributor

ma2ciek commented May 25, 2020

cc @pomek, I guess that you have a better understanding of this tool than me.

@henok-tesfaye
Copy link
Author

Thanks, I integrated with react app rewired. I will have a package which rewires CRA App with CKEditor without any need of configuration soon.

@sharpar
Copy link

sharpar commented Jun 29, 2020

@henok-tesfaye any updates on the package? We're looking for ways to integrate without ejecting too. Thanks!

@sharpar
Copy link

sharpar commented Sep 8, 2020

For anyone who is trying to integrate CKEditor into their React app without ejecting

We used customize-cra (https://github.com/arackaf/customize-cra)

config-overrides.js:

const { addWebpackModuleRule, adjustStyleLoaders, override } = require('customize-cra');
// Use the `tap` function to output the config files for debugging.
// const { addWebpackModuleRule, adjustStyleLoaders, override, tap} = require('customize-cra');
const { styles } = require('@ckeditor/ckeditor5-dev-utils');

// These are copies of the regexes defined in CRA's Webpack config:
// eslint-disable-next-line max-len
// https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/config/webpack.config.js#L63
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;

const forceToArray = (value) => {
    let arr = value;

    // If this is not an array, but is a defined value, put it in an array.
    // Otherwise, return empty array.
    if (!Array.isArray(arr)) {
        arr = arr ? [arr] : [];
    }

    return arr;
};

const adjustFileLoader = () => (config) => {
    // Function that we'll call on each rule, which will modify the rule
    // if it is a rule for file-loader.
    const updateIfFileLoaderRule = (rule) => {
        // The `loader` property contains the filepath to the loader.
        if (/file-loader/.test(rule.loader)) {
            console.log('customize-cra: Update exclude rules for file-loader');
            rule.exclude = [
                // Ensure we keep any existing exclude rules.
                ...forceToArray(rule.exclude),
                /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
                /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/
            ];
        }
    };

    config.module.rules.forEach(rule => {
        if (Array.isArray(rule.oneOf)) {
            // This rule contains an array of rules, so look through each of those.
            rule.oneOf.forEach(updateIfFileLoaderRule);
        } else {
            // This is a standalone rule.
            updateIfFileLoaderRule(rule);
        }
    });

    return config;
};

module.exports = override(
    // Outputs current config to "customize-cra--before.log", with a prepended message
    // tap({ dest: 'customize-cra--before.log', message: 'Before changes for CKEditor' }),

    // -------------------------------------------------------------------------
    // BEGIN: Webpack modifications for loading CKEditor
    // -------------------------------------------------------------------------
    /* eslint-disable max-len */
    // These are described in detail here:
    // https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/react.html#modifying-webpack-configuration
    // Helpful Medium post:
    // https://medium.com/@adamerose/using-ckeditor-5-in-create-react-app-without-ejecting-cc24ffb3fd9c
    /* eslint-enable max-len */

    // (1) Add new rules
    addWebpackModuleRule({
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
        use: ['raw-loader'],
    }),
    addWebpackModuleRule({
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
        use: [
            {
                loader: 'style-loader',
                // The options are slightly different from what CKE has on their
                // integration guide, b/c we have an older version of style-loader
                // installed for some reason (v0.23.1, instead of v1+).
                options: {
                    singleton: true,
                    attrs: {
                        'data-cke': true
                    }
                }
            },
            {
                loader: 'postcss-loader',
                options: styles.getPostCssConfig({
                    themeImporter: {
                        themePath: require.resolve('@ckeditor/ckeditor5-theme-lark')
                    },
                    minify: true,
                })
            }
        ]
    }),

    // (2) Modify config for css loaders
    adjustStyleLoaders((loader) => {
        // Exclude CKE theme files from loaders that have `test: cssRegex` or
        // `test: cssModuleRegex`.
        if (loader.test instanceof RegExp &&
            (loader.test.toString() === cssRegex.toString() ||
                loader.test.toString() === cssModuleRegex.toString())
        ) {
            console.log(`customize-cra: Update exclude rules for ${loader.test.toString()}`);
            loader.exclude = [
                // Ensure we keep any existing exclude rules.
                ...forceToArray(loader.exclude),
                /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
            ];
        }
    }),

    // (3) Modify config for file loaders
    adjustFileLoader()

    // Outputs final config to "customize-cra--after.log", with a prepended message
    // tap({ dest: 'customize-cra--after.log', message: 'After changes for CKEditor' }),
);

CKEditor.js:

import React from 'react';

import CKEditor from '@ckeditor/ckeditor5-react';

// NOTE: Use the editor from source (not a build)!
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';

import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';

const editorConfiguration = {
    plugins: [Essentials, Bold, Italic, Paragraph],
    toolbar: ['bold', 'italic']
};

export default function CKEditorTest() {
    return (
        <CKEditor
            editor={ClassicEditor}
            config={editorConfiguration}
            data="<p>Hello from CKEditor 5!</p>"
            onInit={editor => {
                // You can store the "editor" and use when it is needed.
                console.log('Editor is ready to use!', editor);
            }}
            onChange={(event, editor) => {
                const data = editor.getData();
                console.log({ event, editor, data });
            }}
            onBlur={(event, editor) => {
                console.log('Blur.', editor);
            }}
            onFocus={(event, editor) => {
                console.log('Focus.', editor);
            }}
        />
    );
}

/cc @17cliu

@Mgsy
Copy link
Member

Mgsy commented Oct 8, 2020

Currently, it's not possible to integrate CKEditor 5 with React from the source, however, we'll be thinking about introducing a convenient way to achieve it. Please, refer to the proper issue - #170.

@Mgsy Mgsy closed this as completed Oct 8, 2020
@qathom
Copy link

qathom commented Nov 9, 2020

@sharpar - your solution is working on my side, thanks a lot for sharing this!
I found other alternatives that were much more time-consuming and difficult to implement.

@sharpar
Copy link

sharpar commented Nov 15, 2020

@qathom awesome! Glad it helped! 🥳

@vishalkrv
Copy link

@sharpar Thanks, small changes to your solution and it worked. Here is the complete code

const {
  addWebpackModuleRule,
  adjustStyleLoaders,
  override,
} = require("customize-cra");
const { styles } = require("@ckeditor/ckeditor5-dev-utils");

const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;

const forceToArray = (value) => {
  let arr = value;

  // If this is not an array, but is a defined value, put it in an array.
  // Otherwise, return empty array.
  if (!Array.isArray(arr)) {
    arr = arr ? [arr] : [];
  }

  return arr;
};

const adjustFileLoader = () => (config) => {
  // Function that we'll call on each rule, which will modify the rule
  // if it is a rule for file-loader.
  const updateIfFileLoaderRule = (rule) => {
    // The `loader` property contains the filepath to the loader.
    if (/file-loader/.test(rule.loader)) {
      console.log("customize-cra: Update exclude rules for file-loader");
      rule.exclude = [
        // Ensure we keep any existing exclude rules.
        ...forceToArray(rule.exclude),
        /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
        /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
      ];
    }
  };

  config.module.rules.forEach((rule) => {
    if (Array.isArray(rule.oneOf)) {
      // This rule contains an array of rules, so look through each of those.
      rule.oneOf.forEach(updateIfFileLoaderRule);
    } else {
      // This is a standalone rule.
      updateIfFileLoaderRule(rule);
    }
  });

  return config;
};

module.exports = override(
  // Outputs current config to "customize-cra--before.log", with a prepended message
  // tap({ dest: 'customize-cra--before.log', message: 'Before changes for CKEditor' }),

  // -------------------------------------------------------------------------
  // BEGIN: Webpack modifications for loading CKEditor
  // -------------------------------------------------------------------------
  /* eslint-disable max-len */
  // These are described in detail here:
  // https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/react.html#modifying-webpack-configuration
  // Helpful Medium post:
  // https://medium.com/@adamerose/using-ckeditor-5-in-create-react-app-without-ejecting-cc24ffb3fd9c
  /* eslint-enable max-len */

  // (1) Add new rules
  addWebpackModuleRule({
    test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
    use: ["raw-loader"],
  }),
  addWebpackModuleRule({
    test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
    use: [
      {
        loader: "style-loader",
        options: {
          injectType: "singletonStyleTag",
          attributes: {
            "data-cke": true,
          },
        },
      },
      "css-loader",
      {
        loader: "postcss-loader",
        options: {
          postcssOptions: styles.getPostCssConfig({
            themeImporter: {
              themePath: require.resolve("@ckeditor/ckeditor5-theme-lark"),
            },
            minify: true,
          }),
        },
      },
    ],
  }),
  // (2) Modify config for css loaders
  adjustStyleLoaders((loader) => {
    // Exclude CKE theme files from loaders that have `test: cssRegex` or
    // `test: cssModuleRegex`.
    if (
      loader.test instanceof RegExp &&
      (loader.test.toString() === cssRegex.toString() ||
        loader.test.toString() === cssModuleRegex.toString())
    ) {
      console.log(
        `customize-cra: Update exclude rules for ${loader.test.toString()}`
      );
      loader.exclude = [
        // Ensure we keep any existing exclude rules.
        ...forceToArray(loader.exclude),
        /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
      ];
    }
  }),

  // (3) Modify config for file loaders
  adjustFileLoader()

  // Outputs final config to "customize-cra--after.log", with a prepended message
  // tap({ dest: 'customize-cra--after.log', message: 'After changes for CKEditor' }),
);

@yanghoxom
Copy link

@sharpar @vishalkrv
Do you have any idea for a config-overrides file without customize-cra?
customize-cra have a long time from last update,
the latest version of CKEditor5 do not support customize-cra anymore (it required node 16, and with node 16 require postcss-loader higher version, higher version change some thing related options and make your config can not work)

@Witoso
Copy link
Member

Witoso commented May 25, 2023

@memsenpai did you check our tutorial?

@DeltekDavid
Copy link

DeltekDavid commented May 10, 2024

Any help for a project that uses react-app-rewired? The above solutions are for customize-cra. We followed the React integration tutorial, and it worked well in a new standalone CRA project. Now we are trying to integrate into our existing app. Thanks

@DeltekDavid
Copy link

@henok-tesfaye we also use react-app-rewired. Can you share what you did to get it to build with CKEditor5?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pending:feedback This issue is blocked by necessary feedback. squad:integrations type:question
Projects
None yet
Development

No branches or pull requests