Secure Document Editing with Role-Based Toolbars in React Word Editor


TL;DR: Legal workflows require secure, role-specific Word document editing. This guide shows how to use Syncfusion® React DOCX Editor to implement role-based toolbar customization using ASP.NET Core for backend role management and React for frontend integration, improving usability and access control.
Introduction
Legal teams often struggle with cluttered document interfaces that expose unnecessary tools to every user, increasing the risk of accidental edits and slowing down workflows. In high-stakes environments like law firms, confidentiality and precision are critical; yet many document editors lack the flexibility to tailor the user experience based on roles. Syncfusion’s Document Editor solves this challenge with dynamic, role-based toolbar customization.
Why toolbar customization matters
It’s essential to tailor the user interface based on each user’s role to ensure efficiency and confidentiality. Syncfusion® React DOCX Editor or Word Editor offers a powerful and flexible API that allows developers to customize the toolbar dynamically. This ensures that the user sees only the tools relevant to their responsibilities, reducing clutter and minimizing the risk of unauthorized actions.
In this blog post, we’ll discuss a practical example of implementing role-based toolbar customization using Syncfusion® React Word Editor. You’ll learn how to configure the toolbar for different user roles and enhance usability and security in your Word document editing applications.
Role-based toolbar configuration
In a typical legal document workflow, different users interact with the document in distinct ways based on their responsibilities:
Lawyer: Drafts and edits the legal notice.
Reviewer: Adds comments and tracks changes.
Client: Views the document and adds a digital signature.
Administrator: Has full access to all tools for auditing and publishing.
To support this, the toolbar in the Syncfusion® Word Editor can be customized to show only the relevant tools for each role. Below is a breakdown of the toolbar items and content access for each user type:
Role | Access Description | Accessible Toolbar Items |
Lawyer | Lawyers have access to the entire toolbar, allowing them to draft, edit, and format the document extensively, including inserting tables, links, and structured content. | New, Open, Undo, Redo, Image, Table, Link, Bookmark, Table of Contents, Header, Footer, Page Setup, Page Number, Break, Insert Footnote, Insert Endnote, Find, Comments, Track Changes, Local Clipboard, Restrict Editing, Form Fields, Update Fields, Content Control, XML Mapping Pane, Download, Print |
Reviewer | Reviewers can access tools for commenting, tracking changes, and reviewing the document without editing core content. | Find, Comments, Track Changes, Download, Print |
Client | Clients are provided with a minimal toolbar, allowing them to review the document, add comments, and download or print the file. | Find, Comments, Download, Print |
Administrator | Administrators have full access to all toolbar items, enabling them to audit, edit, and finalize the document as needed. | New, Open, Undo, Redo, Image, Table, Link, Bookmark, Table of Contents, Header, Footer, Page Setup, Page Number, Break, Insert Footnote, Insert Endnote, Find, Comments, Track Changes, Local Clipboard, Restrict Editing, Form Fields, Update Fields, Content Control, XML Mapping Pane, Download, Print |
Setting up ASP.NET Core backend
To manage user authentication and securely handle document access based on user roles, you’ll need a backend service. In this example, we’ll implement the necessary endpoints using ASP.NET Core Web API.
Step 1: Create a new ASP.NET Core Web API project
Create an ASP.NET Core Web API project using Visual Studio or the .NET CLI. This project will be the backend for managing user authentication and related operations.
Step 2: Create API endpoints
Next, create a controller named AuthenticationController.cs under the Controllers folder. This controller will expose two key endpoints:
Register: Allows new users to register by providing credentials such as a username, email ID, and password.
Login: Authenticates existing users using their email ID and password.
These endpoints will internally handle the logic for securely storing and retrieving user data from a users.json file.
AuthenticationController.cs
// Register the new user with the credentials like Username, Email ID and Password
[HttpPost("register")]
public IActionResult Register([FromBody] User newUser)
{
var Users = LoadUsers();
if (Users.Any(u => u.Username == newUser.Username))
{
return BadRequest(new { message = "Username already exists" });
}
if (Users.Any(u => u.Email == newUser.Email))
{
return BadRequest(new { message = "Email already registered" });
}
newUser.Password = BCrypt.Net.BCrypt.HashPassword(newUser.Password);
Users.Add(newUser);
var json = JsonSerializer.Serialize(Users, new JsonSerializerOptions { WriteIndented = true });
System.IO.File.WriteAllText(_userFilePath, json);
return Ok(new { message = "User registered successfully" });
}
// Existing user can login using the credentials such as Email ID and password
[HttpPost("login")]
public IActionResult Login([FromBody] User login)
{
var Users = LoadUsers();
var user = Users.FirstOrDefault(u => u.Email == login.Email);
if (user == null || !BCrypt.Net.BCrypt.Verify(login.Password, user.Password))
{
return Unauthorized(new { message = "Invalid credentials" });
}
return Ok(new { username = user.Username, email = user. Email });
}
Setting up React frontend
Step 1: Create a React app and add a dependency
Create a new React application and integrate the Syncfusion® Word Editor component. This integration enables role-based interactions, allowing the application to dynamically adjust available features based on the user’s assigned role.
Step 2: Implement the authentication form
Create a new JavaScript file named Authentication.js to implement the user authentication form and its associated operations. This component will handle user registration and login, capturing credentials such as email and password, and communicating with the backend API for authentication.
Authentication.js
// Authenticating the users with credentials like Email ID and password. Registering new users.
function Authentication() {
// State hooks for managing authentication
const [user, setUser] = useState(null);
const [isRegister, setIsRegister] = useState(false);
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
// Effect to load user from localStorage on component mount
useEffect(() => {
const saved = localStorage.getItem("user");
if (saved) {
setUser(JSON.parse(saved));
}
}, []);
// Logout user and clear state
const handleLogout = () => {
localStorage.removeItem("user");
setUser(null);
};
// Validate email format using a regular expression
const validateEmail = (email) => /\S+@\S+\.\S+/.test(email);
// Handle login form submission
const handleLogin = async (event) => {
event.preventDefault();
setMessage("");
const form = event.currentTarget;
const email = form.elements.email.value.trim();
const password = form.elements.password.value.trim();
if (!validateEmail(email)) {
setMessage("❌ Please enter a valid email.");
return;
}
try {
// Make a POST request to the login API
const response = await fetch(`${hostURL}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (response.ok) {
const userData = await response.json();
localStorage.setItem("user", JSON.stringify(userData));
setUser(userData); // Update user state directly
} else {
setMessage("❌ Invalid email or password.");
}
} catch (error) {
setMessage("⚠ Server error during login.");
}
};
// Handle registration form submission
const handleRegister = async (e) => {
e.preventDefault();
setMessage("");
const form = e.currentTarget;
const username = form.elements.username.value;
const email = form.elements.email.value;
const password = form.elements.password.value;
if (!validateEmail(email)) {
setMessage("❌ Please enter a valid email.");
return;
}
try {
// Make a POST request to the registration API
const register = await fetch(`${hostURL}/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, email, password }),
});
const data = await register.json();
if (register.ok) {
setMessage("✅ Registration successful. You can now log in.");
setIsRegister(false);
setUsername("");
} else {
setMessage(`❌ ${data.message || "Registration failed"}`);
}
} catch {
setMessage("⚠ Server error during registration.");
}
};
// Render authentication form if not logged in
const renderAuthForm = () => {
return (
<div className="auth-container">
<h2 className="auth-title">{isRegister ? "Register" : "Login"}</h2>
<form onSubmit={isRegister ? handleRegister : handleLogin}>
{isRegister && (
<>
<label className="auth-label" htmlFor="username">Username:</label>
<input
id="username"
type="text"
name="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
className="auth-input"
placeholder="Your username"
/>
</>
)}
<label className="auth-label" htmlFor="email">Email:</label>
<input
id="email"
type="email"
name="email"
required
className="auth-input"
placeholder="you@example.com"
/>
<label className="auth-label" htmlFor="password">Password:</label>
<input
id="password"
type="password"
name="password"
required
className="auth-input"
placeholder="Your password"
/>
<button
type="submit"
className="auth-button primary"
>
{isRegister ? "Register" : "Login"}
</button>
</form>
<button
className="auth-button secondary"
onClick={() => {
setIsRegister(!isRegister);
setMessage("");
setUsername("");
}}
style={{ marginTop: 12, cursor: "pointer" }}
>
{isRegister ? "Already have an account? Log in" : "Don't have an account? Register"}
</button>
{message && <p className="auth-message" style={{ marginTop: 12 }}>{message}</p>}
</div>
);
};
// Render DocumentEditorMain if user is authenticated, otherwise render auth form
return user ? (
<DocumentEditorMain user={user} onLogout={handleLogout} />
) : (
renderAuthForm()
);
}
export default Authentication;
The authentication form will appear as follows:
Authentication form in the client-side
Note: You can further customize this authentication flow by integrating it with your application’s user management system. This allows you to dynamically assign toolbar actions based on user roles after login.
Step 3: Implement Word Editor operations
Create a new JavaScript file (DocumentEditor.js) to implement the React Word Editor functionality. This includes displaying customized toolbar options based on user roles and enabling them to perform their permitted actions, as shown below.
DocumentEditor.js
import { useEffect, useRef } from 'react';
import { DocumentEditorContainerComponent } from '@syncfusion/ej2-react-documenteditor';
function DocumentEditor({ user, onLogout }) {
let defaultDocument = '';
// Configure toolbar items based on user role
const toolbarConfig = {
Lawyer: [
'New', 'Open', 'Separator', 'Undo', 'Redo', 'Separator', 'Image', 'Table',
'Hyperlink', 'Bookmark', 'TableOfContents', 'Separator', 'Header', 'Footer',
'PageSetup', 'PageNumber', 'Break', 'InsertFootnote', 'InsertEndnote',
'Separator', 'Find', 'Separator', 'Comments', 'TrackChanges', 'LocalClipboard',
'RestrictEditing', 'Separator', 'FormFields', 'UpdateFields', 'ContentControl',
'XML Mapping'
],
Paralegal: [
'New', 'Open', 'Separator', 'Undo', 'Redo', 'Separator', 'Image', 'Table',
'Hyperlink', 'Bookmark', 'TableOfContents', 'Separator', 'Header', 'Footer',
'PageSetup', 'PageNumber', 'Break', 'InsertFootnote', 'InsertEndnote',
'Separator', 'Find', 'Separator', 'Comments', 'TrackChanges', 'LocalClipboard',
'RestrictEditing', 'Separator', 'FormFields', 'UpdateFields', 'ContentControl',
'XML Mapping'
],
Client: ['Comments', 'Find'],
Reviewer: ['Comments', 'Find', 'TrackChanges'],
Admin: [
'New', 'Open', 'Separator', 'Undo', 'Redo', 'Separator', 'Image', 'Table',
'Hyperlink', 'Bookmark', 'TableOfContents', 'Separator', 'Header', 'Footer',
'PageSetup', 'PageNumber', 'Break', 'InsertFootnote', 'InsertEndnote',
'Separator', 'Find', 'Separator', 'Comments', 'TrackChanges', 'LocalClipboard',
'RestrictEditing', 'Separator', 'FormFields', 'UpdateFields', 'ContentControl',
'XML Mapping'
]
};
// Create references for title bar and container
const titleBarRef = useRef(null);
const containerRef = useRef(null);
// Effect to initialize or update the word editor when the user changes
useEffect(() => {
if (user && containerRef.current) {
convertDocxToSfdt();
containerRef.current.documentEditor.documentName = 'Document';
containerRef.current.documentEditor.currentUser = user.email;
// Update document title when changes are made
containerRef.current.documentChange = () => {
titleBarRef.current.updateDocumentTitle();
containerRef.current.documentEditor.focusIn();
};
if (!titleBarRef.current) {
titleBarRef.current = new TitleBar(
document.getElementById('documenteditor_titlebar'),
containerRef.current.documentEditor,
true
);
titleBarRef.current.updateDocumentTitle();
}
containerRef.current.toolbarItems = toolbarConfig[user.username];
}
}, [user]);
// Convert GitHub Raw document to SFDT and load in Editor
const convertDocxToSfdt = async () => {
try {
const docxResponse = await fetch(
'https://raw.githubusercontent.com/SyncfusionExamples/Role-Based-Toolbar-Customization-in-Syncfusion-Document-Editor-for-Legal-Workflows/master/Client-side/public/docs/Legal_Notice.docx'
);
const docxBlob = await docxResponse.blob();
const formData = new FormData();
formData.append('files', docxBlob, 'Legal_Notice.docx');
const importResponse = await fetch(
'https://ej2services.syncfusion.com/production/web-services/api/documenteditor/Import',
{
method: 'POST',
body: formData,
}
);
if (importResponse.ok) {
defaultDocument = await importResponse.text();
containerRef.current.documentEditor.open(defaultDocument);
} else {
console.error(`Failed to import document: ${importResponse.statusText}`);
}
} catch (error) {
console.error('Error converting document:', error);
}
};
return (
<div>
<div
className="main-titlebar"
style={{
display: 'flex',
justifyContent: 'space-between',
padding: '8px 12px',
alignItems: 'center',
background: '#f0f0f0',
}} >
<p className="welcome-text" style={{ margin: 0 }}>
Welcome, {user.username || user.email}
</p>
<button className="logout-btn" onClick={onLogout}>
Logout
</button>
</div>
<div id="documenteditor_titlebar" className="e-de-ctn-title"></div>
<style>
{`
.e-toolbar-items {
display: flex !important;
justify-content: center !important;
}
`}
</style>
<DocumentEditorContainerComponent
ref={containerRef}
id="container"
height="calc(100vh - 92px)"
enableToolbar={true}
/>
</div>
);
}
export default DocumentEditor;
Below are visual examples of how the toolbar appears for each role:
Lawyer:
Toolbar appearance for Lawyer
Reviewer:
Toolbar appearance for Reviewer
Client:
Toolbar appearance for client
Administrator:
Toolbar appearance for Administrator
Testing user roles
To test the application, run the server-side (ASP.NET Core API) first, followed by the client-side (React) application. Use the following login credentials for each user role:
Role | Password | |
Lawyer | lawyer@legal.com | 123456 |
Reviewer | reviewer@legal.com | |
Client | client@legal.com | |
Administrator | admin@legal.com |
GitHub reference
For the complete working projects, refer to the GitHub demo.
Conclusion
Customizing the toolbar in the Syncfusion® React Word Editor based on user roles enhances usability and reinforces role-based access control, crucial for workflows involving sensitive content, such as legal documentation. This strategy ensures that each user enjoys a focused, secure editing experience, minimizing the risk of unauthorized changes and boosting overall productivity.
Whether you’re developing a legal platform, an educational solution, or a corporate document system, implementing role-based UI customization is a best practice that adds both refinement and security to your application.
The new version is available for current customers to download from the License and Downloads page. If you are not a Syncfusion ® customer, you can try our 30-day free trial for our newest features.
You can also contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!
Related Blogs
Subscribe to my newsletter
Read articles from syncfusion directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

syncfusion
syncfusion
Syncfusion provides third-party UI components for React, Vue, Angular, JavaScript, Blazor, .NET MAUI, ASP.NET MVC, Core, WinForms, WPF, UWP and Xamarin.