Uploading a PDF looks simple until the application needs to do something useful with it.
A user selects a document, watches a progress bar reach 100%, and expects an answer. Behind the interface, the system may still need to validate the file, scan it, extract text, recognize scanned pages, preserve tables, and prepare content for an AI model.
A reliable document workflow makes those stages visible and recoverable.
For React applications, the central design decision is to separate the browser’s responsibilities from the processing pipeline. React manages selection, feedback, and interaction. Backend services control authorization, storage, extraction, and AI execution.
This guide explains that architecture, the implementation decisions that matter, and how GeekyAnts, AWS, Microsoft, Google Cloud, and Adobe approach relevant parts of document intelligence.
What should a document-processing architecture include?
A practical architecture has five responsibilities:
| Component | Responsibility |
|---|---|
| React interface | File selection, client-side checks, progress, status, and result review |
| Application API | Authentication, authorization, upload sessions, and document records |
| Private object storage | Original files and generated artifacts |
| Background workers | Validation, scanning, extraction, OCR, and AI processing |
| Status and results API | Durable job state, errors, citations, and authorized access |
The browser should not hold privileged storage credentials or AI-provider secrets.
For small files and simple workloads, uploading through an application server can be sufficient. For larger documents or greater concurrency, direct uploads to private object storage can reduce the amount of file traffic passing through that server.
In either design, accepting a file and finishing its analysis are separate events.
How should React represent upload and processing states?
Avoid representing the entire workflow with one isLoading boolean.
A document can finish uploading while still waiting for extraction. It can also fail during analysis even though storage succeeded.
An illustrative TypeScript model makes those distinctions explicit:
type DocumentState =
| { status: "idle" }
| { status: "uploading"; progress: number | null }
| { status: "queued"; documentId: string }
| {
status: "processing";
documentId: string;
stage: "scanning" | "extracting" | "analyzing";
}
| { status: "ready"; documentId: string }
| {
status: "failed";
documentId?: string;
message: string;
retryable: boolean;
};For a single document, ordinary component state may be enough. For multiple files and increasingly complex transitions, useReducer can centralize update logic. React’s documentation specifies that reducers should remain pure, so network requests belong outside the reducer.
Use a stable document identifier to reconcile backend updates. After a page refresh, the interface should recover processing status from the server rather than assume the job disappeared.
How should file selection and validation work?
Start with a labelled native file input. Drag-and-drop can supplement it, but should not be the only interaction.
<label htmlFor="document">Choose a document</label>
<input
id="document"
type="file"
accept=".pdf,.docx,.txt"
onChange={handleSelection}
/>
<p id="upload-help">
Supported formats: PDF, DOCX, and TXT.
</p>Connect the help text with aria-describedby="upload-help" in the final component. Display the actual limits enforced by the product.
Client-side checks provide quick feedback for empty files, unsupported extensions, and oversized uploads. They do not establish that a file is safe.
The backend must independently validate uploaded content. OWASP recommends an allowlist of extensions, file-type validation, generated storage names, size limits, authorization, and antivirus or sandbox inspection where available. It explicitly warns against trusting the submitted Content-Type header.
For document formats that contain compressed content, also constrain extraction resources. A small uploaded file can expand into a much larger processing workload.
How do signed uploads work?
A direct-upload workflow can follow this sequence:
- The browser requests an upload session from the application API.
- The server authenticates the user and creates a document record.
- The server returns a short-lived upload URL for a unique storage key.
- The browser uploads the file.
- The server verifies the stored object before scheduling processing.
Amazon S3 documents that presigned URLs permit time-limited access without giving the recipient AWS credentials. It also notes that URLs can be reused until expiration and that uploading to an existing key replaces the object.
Those details affect application design. Generate unique keys, keep URLs out of analytics and logs, and do not treat them as inherently single-use.
Before processing, bind the job to the validated object version or an immutable processing copy. Otherwise, a still-valid upload URL could replace content after validation.
A possible application API contract is:
| Endpoint | Purpose |
|---|---|
POST /api/documents | Create a document and upload session |
POST /api/documents/:id/complete | Verify upload and schedule processing |
GET /api/documents/:id | Return current status |
GET /api/documents/:id/result | Return authorized results |
DELETE /api/documents/:id | Request deletion of the document and derivatives |
These are proposed application endpoints, not built-in React APIs. Every endpoint must verify access to the requested document.
How can React display real upload progress?
Browser XMLHttpRequest exposes upload progress events. It is useful when the interface needs byte-transfer feedback. MDN also notes that attaching upload listeners affects cross-origin requests, making correct CORS configuration important.
The following helper illustrates a signed PUT upload:
type UploadOptions = {
url: string;
file: File;
headers: Record<string, string>;
onProgress: (percent: number | null) => void;
};
function uploadFile(options: UploadOptions) {
const xhr = new XMLHttpRequest();
const promise = new Promise<void>((resolve, reject) => {
xhr.upload.addEventListener("progress", (event) => {
options.onProgress(
event.lengthComputable
? Math.round((event.loaded / event.total) * 100)
: null
);
});
xhr.addEventListener("load", () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
} else {
reject(new Error(`Upload failed: HTTP ${xhr.status}`));
}
});
xhr.addEventListener("error", () => {
reject(new Error("Upload interrupted by a network error."));
});
xhr.addEventListener("abort", () => {
reject(new DOMException("Upload cancelled.", "AbortError"));
});
xhr.addEventListener("timeout", () => {
reject(new Error("Upload timed out."));
});
xhr.open("PUT", options.url);
xhr.timeout = 120_000; // Example policy; tune for expected files.
for (const [name, value] of Object.entries(options.headers)) {
xhr.setRequestHeader(name, value);
}
xhr.send(options.file);
});
return { promise, cancel: () => xhr.abort() };
}The backend should return the exact headers required by its signature. Configure storage CORS for the application’s origin, method, and headers.
This helper covers transfer only. The caller must handle failures, cancellation, component cleanup, and the completion request.
Reaching 100% means the browser has sent the bytes. Wait for a successful HTTP response before confirming upload success, then show processing as a separate stage.
How should documents be extracted and prepared for AI?
Different document types require different treatment.
| Input | Processing approach to evaluate |
|---|---|
| Text-based PDF | Text and layout extraction |
| Scanned PDF or image | OCR, then structure extraction |
| DOCX | Structured parsing of paragraphs and tables |
| Invoice or form | Field extraction with schema validation |
| Long reference document | Structure-aware chunking for retrieval |
Preserve source locations during extraction. A useful record may include the document version, page, section, text, and bounding coordinates when available.
Google Cloud’s Document AI documentation distinguishes OCR, form parsing, layout parsing, classification, and custom extraction. Its layout tooling supports preparing structured content for search and retrieval-augmented generation.
For retrieval, chunk around meaningful boundaries where practical. Separating a table from its column headers or detaching a paragraph from its heading can remove context needed to answer correctly.
Embeddings are not mandatory for every workflow. Extracting a few fields from a short document may require structured parsing and validation without a vector database.
Why should processing run in background jobs?
OCR and model calls can outlast a normal web request. A durable job lets processing continue when the user closes a tab.
Amazon Textract provides a concrete example: asynchronous operations process documents stored in S3, publish completion through SNS, and allow consumers to retrieve results afterward. AWS recommends completion notifications rather than repeatedly calling its result operation to check job status.
The application can maintain its own status record and expose it to React through polling or server-sent events.
Design retries around durable work:
- Make upload completion idempotent so repeated requests do not create duplicate jobs.
- Use a job key that includes the document version and processing configuration.
- Retry transient failures with bounded backoff.
- Preserve completed extraction when only the AI step needs retrying.
- Treat browser-request cancellation separately from cancelling backend work.
For large uploads, evaluate multipart or resumable transfer independently of job retries. Restarting an analysis job should not require sending the original file again.
How should AI results be validated and displayed?
A useful result is more than a generated paragraph.
For extracted fields, validate the response against an expected schema. Preserve missing values as missing rather than encouraging the model to fill gaps.
For question answering, display source references that the backend can resolve to real document locations. A citation should point to stored evidence, not merely a page number invented in model output.
Treat uploaded document text as untrusted content. A document can contain instructions intended to redirect the model. OWASP’s guidance recommends separating untrusted material from trusted instructions, enforcing permissions outside the model, validating tool arguments, and treating outputs as untrusted at downstream boundaries.
For a document assistant, that means document text cannot grant permission to fetch another tenant’s files, send email, or run arbitrary tools.
Likewise, a model’s self-reported confidence is not a calibrated accuracy score. Where mistakes matter, provide a review interface that shows extracted values beside their supporting source.
How are five companies approaching document intelligence?
Public sources demonstrate different parts of this architecture. They do not establish that all five companies use React internally or share the upload implementation described above.
GeekyAnts: Custom document intelligence and RAG integration
GeekyAnts publishes capabilities covering PDF, text, and multimodal ingestion, semantic search, knowledge-base integration, and document automation. It also describes a document intelligence platform called Pillar Engine.
These materials support discussing its application-integration work. They do not document a specific React uploader, storage contract, or retry mechanism. An evaluation should therefore request those implementation details rather than infer them from service descriptions.
AWS: Storage-backed asynchronous processing
AWS combines relevant building blocks through S3 and Textract. Its documented asynchronous flow separates uploaded objects, processing initiation, completion notification, and result retrieval.
For React developers, the transferable pattern is a durable backend job with status updates. The browser does not need to remain connected while extraction runs.
Microsoft: Layout, prebuilt models, and custom extraction
Azure Document Intelligence documents OCR and document-understanding capabilities for text, tables, structure, and key-value pairs, alongside prebuilt and custom models.
A React review interface can present those structured results for correction or approval. The service supplies extraction capabilities; the application still needs authorization, workflow state, and user-facing error handling.
Google Cloud: Processing matched to document structure
Google Cloud Document AI offers processors for digitization, extraction, classification, and splitting. Its documentation guides developers toward different processors for different document requirements.
The relevant lesson is to choose processing according to the task. Search over a handbook and field extraction from an invoice do not require identical pipelines.
Adobe: Preserving PDF structure
Adobe’s PDF Extract API documents extraction of text, tables, figures, and structural information from native and scanned PDFs. Structured JSON supports downstream processing, while Markdown provides another format for content ingestion.
This makes Adobe relevant when the application needs to preserve more than plain text. Its output still requires application-specific validation and presentation.
What should be tested before launch?
Test the workflow at its failure boundaries.
A practical test set includes interrupted uploads, expired URLs, duplicate completion requests, malformed files, scanned pages, provider timeouts, browser refreshes, and unauthorized document access.
Also test deletion while processing is active. A worker should not recreate results for a document the user has deleted.
Track upload success, processing latency, extraction quality, retry frequency, and cost per successfully processed document. Separate OCR costs from model costs so expensive stages can be identified.
A production-ready flow lets users understand three things at every stage: what has finished, what is happening now, and what they can do if something fails. React makes that experience visible; the backend makes it dependable.





















Add Comment