Stop 10 · API IntegrationProjects · 1/3

Blog CMS

Part of the Frontend Developer Roadmap

In short

A CMS is where writers create, edit and publish posts. Building one makes you use almost everything in this phase: forms, uploads, auth, lists and error handling, all in one place.

A content management interface for creating, editing and publishing blog posts — practice for rich forms, file uploads (cover images) and CRUD operations.

The jaadu edits article pages with a red pen and pins a cover photo onto a magazine draft.

Think of it like this

The college magazine office

Students submit articles, the editor fixes them, adds a cover photo, and decides what goes in this month's issue. Your Blog CMS is that office as an app: drafts, edits, images, and a publish button.

Watch it happen

Publishing a post with a cover

Editor UI
API

POST /uploads (cover)

1/6The author picks a cover image. It uploads first as multipart form data.

The big ideas

What you need to know

Full CRUD

Create, read, update and delete posts. Each action maps to POST, GET, PATCH and DELETE.

Cover image upload

Use FormData to upload a cover, then save the returned image URL with the post.

Drafts vs published

A status field lets you filter the list and only show published posts to readers.

Login required

Only logged-in authors can reach the editor. Handle 401 by sending them to login.

How it works

Build plan

  1. 1

    Post list

    Fetch posts with pagination, plus a filter for draft or published.

  2. 2

    Editor form

    Title, body and status fields, with validation and a disabled Save while submitting.

  3. 3

    Cover upload

    Pick, preview and upload an image, then store its URL in the post.

  4. 4

    Edit and delete

    Reuse the same form for editing. Confirm before delete.

  5. 5

    Polish

    Add loading skeletons, error messages and a login gate.

See it in code

Walk through the code

ts
1type PostInput = { title: string; body: string; status: "draft" | "published" };2 3export async function savePost(input: PostInput, id?: number) {4  const res = await fetch(id ? `/api/posts/${id}` : "/api/posts", {5    method: id ? "PATCH" : "POST",6    headers: { "Content-Type": "application/json" },7    body: JSON.stringify(input),8  });9  if (!res.ok) throw new Error(`Save failed (${res.status})`);10  return res.json();11}

1/4The shape of a post the form sends, including its status.

One function for both create and edit. No id means a new post (POST), an id means update that post (PATCH).

Save yourself the pain

Common mistakes

Oops

Building separate create and edit forms that slowly drift apart.

Do this instead

Use one form component. Pass existing values in for editing and leave it empty for new posts.

Oops

Deleting a post on a single click.

Do this instead

Ask for confirmation first, or offer an Undo for a few seconds.

Your mission

Milestone 1: list and create

~45 min

Quick check

In your CMS, which request should update just the title of post 7?

Go deeper

  1. 1Build create/edit/delete flows against a real or mock backend.
  2. 2Add a cover-image upload with a preview before submitting.
  3. 3Implement a draft vs published status toggle.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge