Building a Complete CRUD System with React, TypeScript, Fluent UI, and Vite

Introduction

As developers progress from learning individual React concepts toward building real-world applications, one milestone stands above many others: creating a complete CRUD system.

CRUD stands for:

  • Create
  • Read
  • Update
  • Delete

These four operations form the foundation of most business applications. Whether you are building:

  • Employee Management Systems
  • CRM Platforms
  • ERP Solutions
  • SharePoint Administrative Tools
  • Inventory Systems
  • Ticketing Applications
  • User Management Portals

the underlying architecture almost always revolves around CRUD operations.

In App 81 — Complete CRUD System, we officially enter Block 5 — Complete Applications, where the goal is no longer learning isolated React features but combining everything learned into enterprise-style applications. This app consolidates concepts from the previous 80 projects and introduces a scalable architecture that resembles real-world corporate systems.


Why CRUD Applications Matter

CRUD applications are the backbone of business software.

Most enterprise systems exist primarily to manage data:

Employees
Customers
Products
Orders
Tickets
Projects
Invoices
Assets
Documents

The user needs to:

Create records
Read records
Update records
Delete records

React is particularly well suited for this because its declarative rendering model automatically keeps the user interface synchronized with application state.

Instead of manually manipulating the DOM, React allows developers to focus on data and state.


React Mental Model

The most important concept reinforced by this application is:

UI = Function(State)

When the employee collection changes:

Add Employee
→ State changes
→ React re-renders
→ UI updates

When an employee is removed:

Delete Employee
→ State changes
→ React re-renders
→ UI updates

The developer never manually updates the screen.

React handles the rendering automatically.

This follows the official React guidance:

  • Components should be pure.
  • UI should derive from state.
  • State should be minimal.
  • Derived values should not be stored.

Project Creation

The project begins with Vite.

mkdir bloco05
cd bloco05
npm create vite@latest app81-complete-crud-system -- --template react-ts
cd app81-complete-crud-system
npm install
npm install @fluentui/react-components @fluentui/react-icons

Vite provides:

  • Fast startup
  • Hot Module Replacement
  • Optimized builds
  • Native ES Modules
  • Excellent TypeScript integration

For modern React development, Vite has become one of the preferred solutions.


Project Structure

Enterprise React applications benefit from clear separation of responsibilities.

src/
├── components/
│ ├── EmployeeForm.tsx
│ └── EmployeeList.tsx
├── models/
│ └── Employee.ts
├── data/
│ └── initialEmployees.ts
├── services/
├── styles/
├── App.tsx
├── main.tsx
└── index.css

Each folder has a specific purpose.

FolderPurpose
componentsReusable UI components
modelsTypeScript interfaces
dataMock or static data
servicesFuture API layer
stylesApplication styling

This architecture scales naturally as applications grow.


Modeling Business Data

The Employee model defines the shape of the data.

export interface Employee {
id: number;
name: string;
department: string;
email: string;
}

This interface provides:

  • Type safety
  • IntelliSense
  • Refactoring support
  • Compile-time validation

Without TypeScript, accidental mistakes become easier:

name: 123
email: true

With TypeScript, these errors are detected immediately.


Initial Data

The application starts with mock employees.

export const initialEmployees: Employee[] = [...]

This simulates a future API response.

Later, the exact same UI can consume data from:

  • REST APIs
  • GraphQL
  • Microsoft Graph
  • SharePoint
  • SQL databases

The UI remains almost unchanged.

Only the data source changes.

This is a major architectural advantage.


Controlled Forms

The EmployeeForm component introduces controlled inputs.

Example:

<Input
value={name}
onChange={(_, data) =>
setName(data.value)
}
/>

React becomes the source of truth.

The flow is:

User types
→ onChange fires
→ State updates
→ React re-renders
→ Input displays new value

This predictable cycle is one of React’s greatest strengths.


Why Controlled Components Matter

Controlled forms provide:

  • Validation
  • Predictability
  • State synchronization
  • Easier debugging

Without controlled inputs, form behavior becomes harder to manage.

Enterprise applications almost always rely on controlled forms.


Creating Employees

The Create operation happens here:

onSave({
id: Date.now(),
name,
department,
email,
});

When submitted:

Form
→ Creates Employee Object
→ Calls Parent Callback
→ Updates State
→ React Re-renders

The EmployeeForm does not directly manipulate the employee collection.

Instead, it communicates with the parent component.

This is a key React pattern.


State Ownership

The employee collection lives inside App.tsx.

const [employees, setEmployees] =
useState<Employee[]>(initialEmployees);

This means:

App owns the state.
Children receive data through props.

React calls this:

Lifting State Up

A common beginner mistake is duplicating state in multiple places.

Centralized ownership avoids synchronization problems.


Immutable Updates

Adding an employee uses:

setEmployees(previous => [
...previous,
employee,
]);

Notice:

Old array remains untouched.
New array is created.

React expects immutable updates.

This makes rendering predictable and enables React optimizations.


Deleting Employees

Delete is implemented using filter.

setEmployees(previous =>
previous.filter(
x => x.id !== id
)
);

The process becomes:

Find matching employee
Remove from collection
Create new array
Update state
React re-renders

Again, no DOM manipulation occurs.

Only state changes.


Searching Employees

Search functionality demonstrates derived state.

The application stores:

search

and

employees

Only.

The filtered collection is calculated.

const filteredEmployees = useMemo(...)

This is important.

React recommends avoiding duplicated state.

Bad approach:

employees
filteredEmployees

stored separately.

Good approach:

Store original data
Calculate filtered data

This avoids synchronization bugs.


Why useMemo Was Introduced

The filter operation could run every render.

employees.filter(...)

For small datasets, that is fine.

However, enterprise applications may contain:

1,000 employees
10,000 employees
100,000 employees

useMemo prevents unnecessary recalculations.

useMemo(() => ..., [employees, search]);

The calculation runs only when dependencies change.


Fluent UI Integration

Fluent UI provides the Microsoft design system.

Components used:

  • Card
  • Input
  • Button
  • Field
  • Typography

Benefits include:

  • Accessibility
  • Keyboard navigation
  • Consistent spacing
  • Microsoft styling
  • Responsive behavior

Instead of building controls manually, Fluent UI provides enterprise-ready building blocks.


Component Composition

The application follows this hierarchy:

App
├── EmployeeForm
└── EmployeeList

Responsibilities remain clear:

ComponentResponsibility
AppState management
EmployeeFormEmployee creation
EmployeeListEmployee display
Employee ModelData contract

This separation improves maintainability.


Why No useEffect?

An interesting observation:

This CRUD application does not require useEffect.

Why?

Because:

  • No API calls
  • No timers
  • No browser subscriptions
  • No external synchronization

Everything is internal state.

React Learn explicitly teaches:

You Might Not Need an Effect

Many beginners overuse useEffect.

This application demonstrates that state alone is often sufficient.


Enterprise Evolution Path

This app is intentionally simple.

However, it provides the exact foundation for future enterprise systems.

Possible next evolutions:

Employee CRUD
→ REST API
→ DataGrid
→ Pagination
→ Sorting
→ Authentication
→ Authorization
→ Context API
→ Service Layer
→ Enterprise Dashboard

The architecture remains valid.

Only additional layers are introduced.


Technical Summary

ConceptDescription
CRUDCreate, Read, Update, Delete
useStateEmployee storage
useMemoSearch optimization
Controlled FormsPredictable form handling
Immutable UpdatesSafe state changes
Derived StateFiltered employees
Component CompositionForm + List
TypeScript InterfacesStrong typing
Fluent UIMicrosoft design system
React RenderingUI derived from state

Official Documentation

React

Fluent UI

TypeScript


Current Progress

BlockAppNameStatus
Block 477Reporting SystemCompleted
Block 478Performance SimulatorCompleted
Block 479Layered ArchitectureCompleted
Block 480Mini Framework React EnterpriseCompleted
Block 581Complete CRUD SystemCurrent
Block 582Employee Management SystemNext
Block 583Financial DashboardUpcoming
Block 584Inventory SystemUpcoming
Block 585Kanban BoardUpcoming
Block 586Enterprise Task ManagerUpcoming
Block 587User Management SystemUpcoming
Block 588Administrative PortalUpcoming
Block 589Ticket SystemUpcoming
Block 590Power BI Style DashboardUpcoming
Block 591Report GeneratorUpcoming
Block 592Audit SystemUpcoming
Block 593SharePoint Inspired PortalUpcoming
Block 594Corporate CatalogUpcoming
Block 595Reservation SystemUpcoming
Block 596Mini ERP EnterpriseUpcoming
Block 597Complete CRMUpcoming
Block 598Analytics SystemUpcoming
Block 599Microsoft Admin Center StyleUpcoming
Block 5100React Enterprise Platform FinalFinal Goal

The Complete CRUD System is the first true enterprise-style application of Block 5 and serves as the architectural foundation for the remaining enterprise applications in the ReactLab roadmap.

Edvaldo Guimrães Filho Avatar

Published by