React Post Creation Form: Building the Frontend for Your Content Management System
ReactNode

React Post Creation Form: Building the Frontend for Your Content Management System

Following our successful backend implementation for post creation, it's time to build an intuitive frontend interface that will bring our CMS to life. While we've already added a posts page, today we'll focus on implementing a crucial feature - the post creation form.

In this comprehensive guide, we'll walk through building a dynamic post creation interface using React. We'll create a user-friendly form that includes all essential fields from our MongoDB schema - from basic title and subtitle inputs to meta information and a rich text editor for content management. We'll also implement proper navigation from the posts list to our new creation form, ensuring a seamless user experience. Okay, so let's start:

1. Preparing a new "create-post" route

As was mentioned we already have a "posts" page and now we need to add a top section with "create new post", and "filters" buttons, and also a "search" field.

- create new "posts" folder inside the "views" folder of the "components" directory;

- add new "PostsAction.component.jsx" and "postsAction.styles.scss" files;

- we need to import some icons from the "MUI-icons" and "useNavigate" hook, and the "TextField" component for the search field and then return the JSX component;

import React from "react";
import { useNavigate } from "react-router-dom";
import "./postsAction.styles.scss";
import TextField from '@mui/material/TextField';
import AddIcon from '@mui/icons-material/Add';
import FilterAltIcon from '@mui/icons-material/FilterAlt';
import SearchIcon from '@mui/icons-material/Search';

const PostsAction = () => {
    const navigate = useNavigate();
    return (
        <div className="posts-action">
            <div className="posts-action__buttons">
                <button className="posts-action__buttons--item"
                    onClick={() => navigate("/post-form") }>
                    <AddIcon />
                </button>
                <button className="posts-action__buttons--item">
                    <FilterAltIcon />
                </button>
            </div>
            <div className="posts-action__search">
            <TextField
                label="Search"
                id="outlined-size-small"
                size="small"
                value={}
                onChange={(e) => ()}
                />
                <button className="posts-action__search--button">
                    <SearchIcon />
                </button>
            </div>
        </div>
    );
}

export default PostsAction;

- open the "Posts.jsx" file from the "posts" folder in the "views" directory, import our "PostsAction" component, and add it to the page;

import React from 'react';
import './posts.styles.scss';
import PostsAction from '../../components/views/posts/PostsAction.component';

const Posts = () => {
    return (
        <div className="Posts">
            <PostsAction />
        </div>
    );
}

export default Posts;

- we have to add a new "PostForm.jsx" file into the "post-form" folder in the "views" directory;

- import "PostForm" into our "App.jsx" file, and then register a new route;

<Route path="/" element={<MainLayout />}>
  <Route index element={<Dashboard />} />
  <Route path='analytics' element={<Analytics />} />
  <Route path='posts' element={<Posts />} />
  <Route path="post-form" element={<PostForm />} />
</Route>

Great. In this part, we created a new section in the "posts" page with the control buttons and registered a new "create post" page. Let's go further and start working on a new post creation form.

2. Creating a Post Form Component

- we need to import a few icons, hooks, our loader component, and some "MUI" components;

- we will start from the end, and configure the return JSX section. We need to build a structured form with the text fields for each backend defined values like "title", "subTitle" etc...;

- we have a body key in the post object, it is an array of possible sections (in our case Text or Image), and we will render through an Array method "map", and need to add an "Autocomplete" dropdown of those sections. Our starting component will look like:

import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from 'react-router-dom';
import Loader from "../../components/ui/loader/Loader.component";
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
const postForm = () => {
return (
    <div className="post-form">
        <div className="post-form__container">
            <div className="post-form__container--header">
                <p className="post-form__container--header-text">Header</p>
                <div className="post-form--item">
                    <TextField 
                        className="post-form--item-input"
                        label="Title"
                        id="outlined-size-normal"
                        value={postForm.title}
                        onChange={(e) => setpostForm((prev) => ({ ...prev, title: e.target.value }))}/>
                </div>
                ... Additional Fields ...
            </div>
        </div>
        <div className="post-form__container">
            <div className="post-form__container--header">
                <p className="post-form__container--header-text">Body</p>
                <div className="post-form__body">
                    {sections.map((section, index) => (
                        <div key={index} className="post-form__body--section">
                            {renderSectionComponent(section, index)}
                        </div>
                    ))}
                </div>
                <div className="post-form--item post-form--item--body">
                <Autocomplete
                    disablePortal
                    id="combo-box-demo"
                    options={bodyOptions}
                    sx={{ width: 300 }}
                    value={bodyOptions.find(option => option.value === selectedSection) || null}
                    onChange={(event, newValue) => setSelectedSection(newValue ? newValue.value : null)}
                    renderInput={(params) => <TextField {...params} label="Choose Section Type" />}
                    />
                    <button className="post-form--item--body-button"
                        onClick={handleAddSection}>
                        {loading ? <Loader /> : 'Add Section'}
                    </button>
                </div>
            </div>
        </div>
         ... Settings Section ...
        <div className="post-form__actions">
            <div className="post-form__actions--button">
                <button className="post-form__actions--button-create" 
                    onClick={handleSubmit}>
                    {loading ? <Loader /> : slug ? 'Update Post' : 'Create Post'}
                </button>
            </div>
        </div>
    </div>
);

- let's create a "renderSectionComponent" function that will get type and return a JSX (image or text) editor or image uploader (we will take care of it in the next article);

const renderSectionComponent = (type, index) => {
    switch (slug && typeof type !== "string" ? type.type : type) {
        case 'image':
        return (
            <div className="post-form__container--header">
                ImageUploader
            </div>
        );
        case 'text':
        return (
            <div className="flex-column">
                Editor
            </div>
        );
        default:
        return null;
    }
};

- also we need to define the "selectedSection", and "postForm" value itself, loading;

const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [selectedSection, setSelectedSection] = useState(null);
const [postForm, setpostForm] = useState({
    title: '',
    subTitle: '',
    slug: '',
    status: 'offline',
    archived: false,
    meta: {
        description: '',
        keywords: '',
        schema: '',
    },
    body: []
});
const bodyOptions = [
    { label: 'Image', value: 'image' },
    { label: 'Text', value: 'text' },
];
const languages = [
    { label: 'Ukrainian', value: 'uk' },
    { label: 'English', value: 'en' },
    { label: 'Spanish', value: 'es' },
];

- and our final touch, for now, will be creating a "handleSubmit" function that will get the form data, push created value, handle loader, and notification and send it to the server;

const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    postForm.created = {
        date: {
            day: String(new Date().getDate()).padStart(2, '0'),
            month: String(new Date().getMonth() + 1).padStart(2, '0'),
            year: String(new Date().getFullYear()),
            time: new Date().toTimeString().split(' ')[0],
        }
    };
    try {
        const response = await createNewPost({ post: postForm });
        if (response.status === 200) {
            dispatch(aPushNewNotification({
                type: 'success',
                text: response.message,
            }));
            setpostForm({
                title: '',
                subTitle: '',
                slug: '',
                status: 'offline',
                archived: false,
                meta: {
                    description: '',
                    keywords: '',
                    schema: '',
                },
                body: [],
            });
            setLoading(false);
            navigate('/posts');
        }
    } catch (error) {
        dispatch(aPushNewNotification({
            type: 'error',
            text: response.message,
        }));
        setLoading(false);
        console.log("Error:", error);
    }
};

Let's restart our React app and check the UI result.

Post Form Ui

Looks great, but we still do not have the possibility to upload images and work with the text, I suggest we fix this temporary problem in the next article.

You can find the complete code for this implementation in the archive.

Stay tuned for the next part where we'll transform this basic form into a full-featured post editor with media handling capabilities!

Start the conversation

Show comments

Related

How to Add a QR Code Scanner in Vue.js (Step-by-Step Guide)

How to Add a QR Code Scanner in Vue.js (Step-by-Step Guide)

Starting out in programming is thrilling, yet the number of languages available makes it difficult to decide where to begin.

Building Simple CRM with Vue: Crafting Layouts and Navigation

Building Simple CRM with Vue: Crafting Layouts and Navigation

Starting out in programming is thrilling, yet the number of languages available makes it difficult to decide where to begin.

Full-Stack Blogging CMS: A 17-Part Journey

Full-Stack Blogging CMS: A 17-Part Journey

Starting out in programming is thrilling, yet the number of languages available makes it difficult to decide where to begin.

Series

Categories