- Read Tutorial
- Watch Guide Video
So we're gonna take a step-by-step approach in building this out. It's not going to be connected from a state perspective quite yet so it's not gonna be functional yet, there are a few key state and prop related items that we'll need to implement and we'll dedicate a whole guide just to that.
But for right now, let's just get the form implemented and get it placed directly inside of our actual blog form. So I've zoomed out so if it's a little bit harder to see it's because when we click this modal, I wanna get a accurate representation of the height of the modal.
The screen that I film on, the resolution is so high that usually I like to have it really zoomed in so it's easy to see everything, but if I have it zoomed in all the way, then what's gonna happen is this modal is gonna be overflowing on the screen and it's not gonna represent what you are actually looking at, so I want to try to do that.
And so I've zoomed out and now let's actually open this up and let's get started. So the first thing that I'm going to do is create a dedicated directory just for our form components.
So inside of the components directory, I'm going to create a new folder here called forms and the reason why I'm doing this is because if we ever wanna use this rich text editor somewhere else in the application, I don't wanna tie it so closely to the blog form that it can only be used for blogs and then we'd have to recreate another instance of this someplace else.
It's a much better practice in React to build your components so that they can actually be shared with other features even if those other features are unrelated. So a great example of this would be to say that you wanted to extend your portfolio form to allow you to type in rich text media. If you build this component correctly, you should be able to call it from the portfolio form, the same way you're doing from the blog form.
So that's why I'm creating this abstract forms
directory and I'm gonna create a new file here called rich-text-editor.js
and it's a JavaScript file and inside of here it's gonna be a class component because it's gonna have to maintain its own state. So I'm just gonna use my user snippet here and we're just gonna call this RichTextEditor
.
So for right now, let's just make sure that this is working and we can actually call this from the other component. So I'm just gonna add an h1 tag here called RichTextEditor and duplicate it just so I don't get prettier to smash all of my code together.
rich-text-editor.js
import React, { Component } from 'react'; export default class RichTextEditor extends Component { render() { return ( <div> <h1>RichTextEditor</h1> <h1>RichTextEditor</h1> </div> ); } }
So from the blog form now, I should be able to call this and see our h1's. So I'll say import RichTextEditor from, and oh, I have to obviously go and grab it from the correct directory, I'm in blog, so I need to jump back two forms. So this should be ../forms/rich-text-editor.
blog-form.js
import RichTextEditor from "../forms/rich-text-editor";
Now we can call this and we're not gonna pass any props or anything like that yet, we will be doing that in a future guide. So for right now, let's get this on the page. We have, if you remember when we created our styles, we have a one and a two-column grid, so I'm just going to create a div wrapper here and since I know that we have that style available, I want one-column because I want this rich text editor going all the way from side to side. And now I can create the component itself and call it.
<div className="one-column"> <RichTextEditor /> </div>
So let's test this out and let's see if we have our h1 tags. So now if I click on New Form, you can see I have my RichTextEditor, that is perfect.
Now notice also how this modal is so small that the RichTextEditor is gonna take up a lot of space. So before we go any further, let's also stretch this out so that it has a better height. So I'm going to inspect this element to see exactly what we want to grab and what our override should be.
So you can see we have this ReactModal__Overlay and we have ReactModal__Body--Open. Let's see the exact className that we want to use. It's not the form, so there we go. Okay, so ReactModal__Content, I think that is the class we're gonna wanna use and we can test this out because if I come down into my styles and go all the way down to the bottom and I know I have it zoomed in so much that's kinda hard to see but if I say height and say 80%, this should be what we're looking for.
And let me also just copy this className so that I know what I wanna do for the override. And yes, that is what I'm looking for. So when we click on our little plus icon, we should have the entire body or 80% of the height taken up with the modal. Then that is what we're wanting so let's go test this out.
I'm gonna open up our main style file, we don't have a dedicated modal file yet but whenever I'm building out a new style implementation, the first thing I do usually, especially if I'm like this where I'm just exploring is I just paste in the className directly in main because that way it's called right away and that way I'm not dedicating a whole file to it yet, and then after I make sure the styles are working and set up correctly, then what I can do is then I can go create the dedicated file and take it from there.
So I'm gonna say Modal__Content or, yeah, ReactModal__Content comma and then put a period in front of ReactModal__Content--after-open. I'm not sure if this is exactly what we want yet but that's what we're doing right now, we're just exploring this. I didn't build this feature in so I don't have any notes on this one but let's just test this out. I'm gonna say height: 80%, hit save, and let's test this out.
main.scss
.ReactModal__Content, .ReactModal__Content--after-open { height: 80%; }
I'm gonna hit refresh, it looks like that worked just from our auto reload but let's reload our styles and make sure it worked, and yes, we do. So that's working perfectly.
What we can do now is we can take this code, cut it out, and I'm going to create a dedicated file so this will be, we can just say something like modals and that way all of our custom modal override files or styles can go in there.
So inside of the styles
directory, create a modals.scss
file and then from there, just paste in what we already created, hit save on the modal file and on the main file, test it out, and yes, everything is still working and now we have a modal that actually has enough room for our RichTextEditor and then eventually also for our image uploader.
modals.scss
.ReactModal__Content, .ReactModal__Content--after-open { height: 80%; }
Okay, so that is our modal file and our modal styles. Let's add a few more things before we take a break 'cause I actually want, my goal was for this guide for us to have a real RichTextEditor by the end of it, even if it's not functional yet, at least we can see it. So let's get started on that.
We have inside of our RichTextEditor, we have to import our draft libraries and there are a number of them so let's get started on those. I'm gonna say import from draft.js we have two libraries, two modules we have to import.
One is EditorState spelled out exactly like that with a capital E and a capital S and then the next one is convertToRaw and as you may have noticed, EditorState starts off with a capital E which means that is a component and then the convertToRaw starts off with a lowercase c which means that is most likely a higher order component, so something where we wrap other code up. We're not gonna worry about it at this moment but it's good to just kinda keep an eye on that.
So I'm gonna say that is from draft-js. Okay, the next import that we need to do is the editor itself. So here I'll say import Editor from and this one is from react-draft-wysiwyg, what you see is what you get, I believe I spelled that out correctly. And then from there, we have two more, the first one is import draftToHtml from and then this is draftjs-to-html and then import htmlToDraft from html-to-draftjs.
rich-text-editor.js
import React, { Component } from "react"; import { EditorState, covertToRaw } from "draft-js"; import { Editor } from "react-draft-wysiwyg"; import draftToHtml from "draftjs-to-html"; import htmlToDraft from "html-to-draftjs";
Okay, I know that's a mouthful and no, I did not have those memorized, I do have those imports listed in my notes section on the right hand side to make sure that I spelled them correctly. I'm not a huge fan of the naming convention where they went with draft-js as the main core library but then in other spots, they use draftjs without the dash and that can lead to some confusion.
Just whenever you're building out when you get to the point where you're building your own libraries, make sure you follow a good naming process so that other people can name them and remember them without having to think about which ones have dashes and which ones are together, that's a side note, just a personal preference.
Now that we have that all, let's actually add a constructor. So I'm going to start off with a constructor, we will be taking in props so let's add that now along with super and then props and then from there let's give a base state. So here we're gonna be calling the EditorState.
So this is something that can be a little bit tricky when working with draft-js for the first time is that you can't simply say something like this, I can't say this.state equals editorState and start with an empty string, you will run into a lot of issues with that and it's because the editor has a number of things that are doing behind the scenes and so, they have a dedicated module for this and it is an entire system that manages its state.
That's why we imported this library of editor.state and it has its own set of functions so if you hit dot, you should get some autocomplete here and we want to createEmpty.
As you may have seen, this editor is so big, it has so many potential features in it, you also have some options like if I say create, you would have the ability to create with some default content if you wanted but we wanna createEmpty and then from there, that is a function. So let's actually call it and then that's all we need inside of our state.
rich-text-editor.js
export default class RichTExtEditor extends Component { constructor(props) { super(props); this.state = { editorState: EditorState.createEmpty() } }
Now I'm gonna get rid of these h1 tags and instead of them, I'm going to call the editor component itself and we're going to start without base state so that's the first prop is it needs to know what the base state is and so we're going to pass in our editorState and one little piece of note, right here we have to actually call the import state so you could do this.state.editorState and then let's make sure we're closing off our editor.
Then from there we need to give it a className, so wrapperClassName, that's the second prop, and I'm gonna go with demo-wrapper and the name here does matter because what we're gonna be doing is we're gonna be leveraging the basic styles provided through one of the libraries so these names you're passing in are going to have corresponding styles that are associated and built directly into them. So that's the first one, the next one is gonna be the editorClassname and this one is gonna be demo-editor.
rich-text-editor.js
render() { return ( <div> <Editor editorState={this.state.editorState} wrapperClassName="demo-wrapper" editorClassname="demo-editor" /> ) }
Okay, that was a lot of code here, let's test this out and let's see what, if it's working, if we have any errors, anything like that. So go to Google Chrome, hit Refresh, and click on the plus button. Okay, so we have something, we don't have any errors but this is kinda weird, we have all of these little icons.
So the reason for this is because there are two sides to implementing this RichTextEditor. You can't just pull in the JavaScript, you also have a decent amount of CSS that makes all of this possible. Now thankfully, we do not have to write this, this is the reason why we brought in the react-draft-wysiwyg library.
So what we can do is we can create a dedicated style file and then I'm going to then bring in the styles provided from the library and I think it's like 1,000 lines long so we're not gonna type it out, I'm just gonna bring it directly in. So inside of main, I'm here going to create a new file or a new import called react-draft-wysiwyg, what you see is what you get.
I've misspelled wysiwyg so many times in my life, it's kind of embarrassing but it is a weird acronym. And so I believe, yeah, there we go. Okay, so this is the file name,
main.scss
@import "./react-draft-wysiwyg.scss";
technically you could name this file anything you want but I do like that being accurate. And then if you come into styles, create that file itself, and I am going to go offscreen and get the files. These are from the library so I did not create these or anything like this.
I'm pulling them directly in from the library so in the show notes for this guide, I'll provide a link where you can grab them and paste them in because yeah, as you can see, this is 1,000 lines long. This is the style magic that makes the editor work.
Okay, so if you hit Save now, if you want, you can take a look at it and the reason why I'm pulling all of these styles directly in is because if you wanna make any types of overrides or any changes, you can do them right here. So this gives you a lot of flexibility.
So now let's test this out. Let's go into Google Chrome and I made the mistake, this isn't an error on the code side, I made a mistake of saving the main SCSS file before I saved this file, that's one of the errors that you will see, the easiest way to fix this is I'm gonna open up the server and just run start one more time.
So this is gonna restart the entire NPM local server. I mentioned that earlier in the course is if you call an import especially a SCSS import, a lot of times what will happen is it will throw an error and the only way of fixing that specific error is by restarting the server.
I'm kinda glad that I left that one in there though because if you run into that error, it can be really frustrating because the first time I had that happen, my first thought was, the file is right there but the server doesn't know about it yet and that's the reason why we need to restart it.
So now you can hit Refresh, you can see our error is fixed and if you hit the plus icon, there we go, look at that, we have our entire setup working.
Now it's not functional yet and it's kinda weird how our text box doesn't actually have a border so we still have some cleanup items but we have a rich text editor and right now we can't make any changes to it because we're not working with state and we're not handling changes and those kinds of things but this looks really good, it has all of the features that we wanted. It has the ability to have cool custom headings and to style it and to have custom fonts and font sizes, and even images and emojis.
So this is pretty cool, this is a lot of functionality that really only took about 10-15 minutes to implement, and you'll see the further we go along, we're gonna be able to extend this functionality and it's gonna work just like you'd expect, like you would use with a tool like WordPress or any kind of system that allows you to add custom formatting directly into your application.
So great job if you went through that, I know that was quite a bit of code, but take a break right now and when we come back, we are going to be building out the functionality that's going to connect this rich text editor and allow us to access this data inside of our blog form.