ckeditor / ckeditor5

Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
https://ckeditor.com/ckeditor-5
Other
9.52k stars 3.7k forks source link

Next.js integration #7376

Closed Mgsy closed 4 months ago

Mgsy commented 4 years ago

📝 Provide a description of the new feature

Provide a guide for integrating CKEditor 5 with Next.js.

[Update 2 from @Witoso]

New installation methods shipped in v42.0.0 work seamlessly with Next.js. Check Next.js integration guide.

[Update]

Hello everyone 👋!

We published an article in our docs, about the Next.js integration. I want to thank community members that shared the examples below.

That said, there's still work in front of us to make this integration smoother. And we aim to fix it in its roots. I invite everyone to check our new installation methods RFC: #15502.


If you'd like to see this feature implemented, add a 👍 reaction to this post.

Mgsy commented 4 years ago

Currently, it's not possible to build the editor from the source using Next.js. I think it's related to a webpack configuration and Next.js webpack config requires some adjustments to make it compatible with CKEditor 5.

However, it's possible to run CKEditor 5 build inside Next.js application. To everyone struggling with the integration, please check the below workaround based on StackOverflow answer:

npm install --save @ckeditor/ckeditor5-build-classic @ckeditor/ckeditor5-react
import React, { useState, useEffect, useRef } from 'react'

export default function MyEditor () {
  const editorRef = useRef()
  const [ editorLoaded, setEditorLoaded ] = useState( false )
  const { CKEditor, ClassicEditor } = editorRef.current || {}

  useEffect( () => {
    editorRef.current = {
      CKEditor: require( '@ckeditor/ckeditor5-react' ),
      ClassicEditor: require( '@ckeditor/ckeditor5-build-classic' )
    }
    setEditorLoaded( true )
  }, [] )

  return editorLoaded ? (
    <CKEditor
      editor={ ClassicEditor }
      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 } )
      } }
    />
  ) : (
    <div>Editor loading</div>
  )
}

The above solution uses our official classic editor build, however, you can customize a build "outside of the Next.js integration" and load it the same way. Also, you can use CKEditor 5 online builder to get a ready-to-use build with a set of desired plugins.

seta-hainguyen commented 4 years ago

@Mgsy your solution works fine with basic use but got errors when I integrated with CKEditor Plugins like Easy Image or Alignment. Seems regarding to webpack configurtion:

_error - ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg 1:0 Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

Could not find files for /dashboard in .next/build-manifest.json ModuleParseError: Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders`

FilipaBarroso commented 4 years ago

I was trying to follow the tutorial to create a simple plugin but I'm having the issue mentioned in #7343, because I'm importing the @ckeditor/ckeditor5-ui and @ckeditor/ckeditor5-core packages. I assume the same issue will arise in other packages as well, for anyone working with Next.js.

Could you please provide compiled packages or a workaround for this?

Thanks for your attention.

sethcwhiting commented 4 years ago

@Mgsy Just for context, Filipa is on my team (I'm the guy who originally raised the issue). We were doing great with the online build tool for a while, but we're now at the point where we need to add our own plug-in and the online build has become insufficient for our needs. If there's any way you could push offering compiled versions of your individual packages up your priority list, that would help us out tremendously. Until then, we'll probably need to handle this in some hacky way, which we really don't want to do. Thanks so much!

adrianmaycon commented 4 years ago

How do I enable the upload image option?

wwalc commented 4 years ago

Might be useful: https://medium.com/@aisynurul99/custom-ckeditor-5-next-js-d6ef5cc7dd93 (did not review it)

perezjoyce commented 4 years ago

@Mgsy your solution works fine with basic use but got errors when I integrated with CKEditor Plugins like Easy Image or Alignment. Seems regarding to webpack configurtion:

_error - ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg 1:0 Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

Could not find files for /dashboard in .next/build-manifest.json ModuleParseError: Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders`

I have the same issue. Were you able to resolve this?

hscstudio commented 3 years ago

Solve with this simple step

  1. Install ckeditor yarn install @ckeditor/ckeditor5-react @ckeditor/ckeditor5-build-classic
  2. Wrap ckeditor as a component, create new file src/components/Editor.js
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import CKEditor from '@ckeditor/ckeditor5-react';

const Editor = (props) => {
  return (
    <CKEditor
      editor={ ClassicEditor }
      data={props.value}
      onChange={ (event, editor ) => {
        const data = editor.getData()
        props.onChange(data)
      } }
    />
  )
}

export default Editor;
  1. On src/pages/Home, use component Editor like this
import dynamic from 'next/dynamic'

const Editor = dynamic(() => import('../components/Editor'), { ssr: false })

const Home = () => {
  return (
    <Editor value="" onChange={(v)=> console.log(v)} />
  )
}
export default Home;
jorissparla commented 3 years ago

@hscstudio @Mgsy both solutions result in an unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of MyEditor.

Call Stack createFiberFromTypeAndProps node_modules\react-dom\cjs\react-dom.development.js (25058:0) "dependencies": { "@ckeditor/ckeditor5-build-classic": "^24.0.0", "@ckeditor/ckeditor5-react": "^3.0.0", "next": "10.0.3", "react": "17.0.1", "react-dom": "17.0.1" }

AND

Unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of Editor.

hscstudio commented 3 years ago

@hscstudio @Mgsy both solutions result in an unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of MyEditor.

Call Stack createFiberFromTypeAndProps node_modules\react-dom\cjs\react-dom.development.js (25058:0) "dependencies": { "@ckeditor/ckeditor5-build-classic": "^24.0.0", "@ckeditor/ckeditor5-react": "^3.0.0", "next": "10.0.3", "react": "17.0.1", "react-dom": "17.0.1" }

AND

Unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of Editor.

Show your code?

jorissparla commented 3 years ago

Here it is https://github.com/jorissparla/x. Very basic.

hscstudio commented 3 years ago

Here it is https://github.com/jorissparla/x. Very basic. Maybe error from code in pages/index.js, remove pages/index.js and rename pages/home.js to be pages/index.js

jorissparla commented 3 years ago

Here it is https://github.com/jorissparla/x. Very basic. Maybe error from code in pages/index.js, remove pages/index.js and rename pages/home.js to be pages/index.js

runtime

hscstudio commented 3 years ago

@jorissparla

change in Your packages to this version "@ckeditor/ckeditor5-react": "^1.1.0", and then npm install

but I see working example

https://github.com/nghiaht/nextjs-ckeditor5

jorissparla commented 3 years ago

that worked, Also the second link worked. Need to play around to see what is causing the issues in my basic example. Thank you!!!

jorissparla commented 3 years ago

moving from "@ckeditor/ckeditor5-react": "^2.1.0", to "@ckeditor/ckeditor5-react": "^3.0.0", breaks the example. Bug report https://github.com/ckeditor/ckeditor5/issues/8704

afk-mario commented 3 years ago

Customized editor (with plugins) was really easy following the following docs and using the online builder.

OrionLayton commented 3 years ago

@afk-mario Would you mind sharing how you got CKEditor plugins working with Next? I keep running into the "Global CSS cannot be imported from within node_modules" error when trying to set up ExportPdf.

afk-mario commented 3 years ago

@OrionLayton

import React, { Component } from 'react';
// When you add the ckeditor5 folder you can import it this way
import Editor from 'ckeditor5-custom-build/build/ckeditor';
import { CKEditor } from '@ckeditor/ckeditor5-react'

const editorConfiguration = {
    toolbar: [ 'bold', 'italic' ]
};

function Editor  ()  {
        return (
          <CKEditor
              editor={ Editor }
              config={ editorConfiguration }
              data="<p>Hello from CKEditor 5!</p>"
              onChange={ ( event, editor ) => {
                  const data = editor.getData();
                  console.log( { event, editor, data } );
              }
          />
    );
}

export default Editor;

Finally when you import this component make sure you are skipping server side rendering.


import React from 'react';
import dynamic from 'next/dynamic';

const Editor = dynamic(() => import('./components/editor'), {
  ssr: false,
});

function App = () => {
  return (
    <div>
      <Editor />
    </div>
  );
};

export default App;

If you need plugins outside of the ones that the online builder has support for I think you can create a custom build by yourself but didn't tried that, the process after getting the custom build should be similar.

Emyboy commented 3 years ago

@Mgsy I'm getting window is not defined when I build

and get this during development. image

jeffryang24 commented 3 years ago

From my experiment, I got ReferenceError: window is not defined when running next build if I implemented custom _app.js file with some server-side data fetching method, such as getInitialProps. The build didn't fail when I remove my custom _app.js implementation. Ya... This is actually so annoying... :disappointed:

// // pages/_app.js (got from nextjs documentation: https://nextjs.org/docs/advanced-features/custom-app)
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext) => {
//   // calls page's `getInitialProps` and fills `appProps.pageProps`
//   const appProps = await App.getInitialProps(appContext);
//
//   return { ...appProps }
// }
jeffryang24 commented 3 years ago

Finally, I used cancellablePromise, React Mutable Ref, and dynamic import to solve that. But, currently I'm facing a performance issue when rendering more than 10 CKEditor with dynamic import, this will make the browser unresponsive. Still find a way to minimize this performance issue.

sunkid1995 commented 3 years ago

I was trying to follow the tutorial to create a simple plugin but I'm having the issue mentioned in #7343, because I'm importing the @ckeditor/ckeditor5-ui and @ckeditor/ckeditor5-core packages. I assume the same issue will arise in other packages as well, for anyone working with Next.js.

Could you please provide compiled packages or a workaround for this?

Thanks for your attention.

same issue!

EthanStandel commented 3 years ago

Just wanted to throw my two cents in this thread. I integrated CKEditor into Next using the next/dynamic component with {ssr:false} in the options. However, I was still getting weird errors. I was using it in a page, on src/pages/ckeditor.js just to test. Turns out that utilizing that route breaks CKEditor. Once I renamed that route, everything worked fine.

mtkFreexe commented 3 years ago

Is there any answers for ck editor plugin(Font) integration in Nextjs

jsbimra commented 3 years ago

Just wanted to throw my two cents in this thread. I integrated CKEditor into Next using the next/dynamic component with {ssr:false} in the options. However, I was still getting weird errors. I was using it in a page, on src/pages/ckeditor.js just to test. Turns out that utilizing that route breaks CKEditor. Once I renamed that route, everything worked fine.

Worked for me too! Have you able to get all the options? can we see like (git editor) when we are replying on Git Thread here? I am just seeing Bold and Italic option only. I tried implementing: plugins: [ Essentials, Bold, Italic, Paragraph ], but I get error:

./node_modules/@ckeditor/ckeditor5-clipboard/theme/clipboard.css Global CSS cannot be imported from within node_modules. Read more: https://nextjs.org/docs/messages/css-npm Location: node_modules\@ckeditor\ckeditor5-clipboard\src\dragdrop.js

jsbimra commented 3 years ago

Just wanted to throw my two cents in this thread. I integrated CKEditor into Next using the next/dynamic component with {ssr:false} in the options. However, I was still getting weird errors. I was using it in a page, on src/pages/ckeditor.js just to test. Turns out that utilizing that route breaks CKEditor. Once I renamed that route, everything worked fine.

Worked for me too! Have you able to get all the options? can we see like (git editor) when we are replying on Git Thread here? I am just seeing Bold and Italic option only. I tried implementing: plugins: [ Essentials, Bold, Italic, Paragraph ], but I get error:

./node_modules/@ckeditor/ckeditor5-clipboard/theme/clipboard.css Global CSS cannot be imported from within node_modules. Read more: https://nextjs.org/docs/messages/css-npm Location: node_modules\@ckeditor\ckeditor5-clipboard\src\dragdrop.js

Found it with toolbar options here: https://ckeditor.com/docs/ckeditor5/latest/features/toolbar/toolbar.html#automatic-toolbar-wrapping not sure why those error for plugins! 👍

lazarok09 commented 2 years ago

The 'media embed' plugin works with the React component and Next.js ?

I have a builded editor, i created following ckeditor 5create online builder But, the embed video only shows on the editor, the source code that is generated when i get it on getData() is different

The React component

image

The problem

When the editor convert the embed to a html code, is failing, getting me just this code when i use getData().

<figure class="media"><oembed url="https://www.youtube.com/watch?v=4zAThXFOy2c&amp;feature=emb_title"></oembed></figure>

This is not right at all. It should be like a figure tag, some divs.. a