Document Workflow
Convert an HTML fragment to React JSX without carrying over legacy bugs
Isolate a useful HTML fragment, convert its syntax to JSX, review active code and parser changes, then integrate and test it as a real React component.
Written and tested by SimpleWebUtilsPublished: Reviewed:
How this workflow was checked
Verification started with the guide's “Migrate a profile-name field without retaining a global click handler” fixture in Markup Conversion Workbench. The run followed “Isolate one coherent fragment and its dependencies” through “Redesign active code instead of merely preserving it”, compared the produced result to the documented expectation, and checked the distinct limits behind “Migrating an entire exported page at once” and “Assuming a Babel-valid expression is a working component”.
The fragment used className and htmlFor in JSX, preserved the label-input relationship, and surfaced the global onclick handler for replacement with a component-owned callback.
Problem
Static HTML often depends on global scripts, DOM IDs, inline event strings, document-level metadata, relative asset paths, and CSS loaded elsewhere. A mechanical converter can rename attributes and produce valid JSX while preserving unsafe URLs, obsolete behavior, inaccessible labels, or assumptions that no longer exist inside React. Full-page HTML also follows document parsing rules that differ from a component fragment. A safe migration therefore needs both syntax conversion and an explicit behavior-and-integration review.
Sources and standards
These authoritative references define the formats or security boundaries used in this workflow. Tool-specific verification is documented separately above.
- HTML Living Standard
WHATWG
When to use this
- A designer or prototype provides a reviewed HTML card, form, navigation item, table, or SVG that must move into React.
- A legacy page is being migrated one interaction at a time rather than embedded with raw innerHTML.
- You want to compare HTML attributes, inline styles, SVG properties, and event strings with their JSX equivalents.
- A test or documentation example needs a small syntax-checked JSX fixture derived from HTML.
Steps
- Step 1
Isolate one coherent fragment and its dependencies
Select the smallest section that still solves one user task. Record the CSS selectors, image and font files, form submission target, links, keyboard behavior, focus order, global variables, and scripts it currently depends on. Remove doctype, html, head, and body wrappers unless you are intentionally migrating framework metadata outside the component. Replace secrets, customer data, internal URLs, and production identifiers with deliberate test values.
- Step 2
Capture expected behavior before changing syntax
Open the original fragment in its existing page or a controlled fixture. Note what happens on click, submit, keyboard activation, invalid input, loading failure, and narrow screens. Check accessible names and label relationships. Save representative screenshots or assertions. Without this baseline, syntactically valid JSX can silently lose behavior and there is no reliable comparison.
- Step 3
Convert and compare the normalized tree
Open HTML to JSX in Markup Conversion Workbench and convert the focused fragment. Compare nesting, text, className, htmlFor, data and aria attributes, style values, SVG properties, and self-closing elements. HTML parsing can close malformed tags, insert implied table elements, or reorganize document-level content. Resolve every size, depth, template, or syntax error instead of editing around it in the output.
- Step 4
Redesign active code instead of merely preserving it
Treat warnings for onclick-style handlers, script elements, srcDoc, javascript URLs, and active style values as required review work. Replace globals and DOM queries with props, state, refs, effects, and named event handlers. Use safe application navigation and trusted URLs. If nested HTML is genuinely required, sanitize it at the trust boundary and document why raw rendering is necessary.
- Step 5
Build the actual component contract
Place the JSX expression inside a named component. Define props and TypeScript types, import assets, move styles into the project's chosen system, and decide which inputs are controlled or uncontrolled. Preserve semantic elements, labels, alt text, heading order, and keyboard behavior. Avoid turning every static value into state or keeping inline styles when ordinary CSS is easier to maintain.
- Step 6
Verify in the destination project
Run the formatter, linter, type checker, unit and interaction tests, and a production build. Compare the component with the baseline at desktop and 390px mobile widths, using keyboard-only navigation and an accessibility scanner. Inspect console and network activity, test failed assets and form errors, and confirm no untrusted code or URL was introduced before the migration is merged.
Example
Migrate a profile-name field without retaining a global click handler
Input
<section class="profile-card"><label for="name">Name</label><input id="name" value="Ada"><button onclick="saveProfile()">Save</button></section>Output
Convert class and for syntax, then replace the preserved onClick body with a component-owned handleSave function. Decide whether the input uses value plus onChange or defaultValue, define the save callback as a prop, and test label focus, keyboard activation, validation, and failure feedback.Common mistakes
Migrating an entire exported page at once
Full documents mix metadata, scripts, styles, navigation, and several unrelated tasks. Convert one coherent fragment at a time so parser normalization and behavioral changes can be reviewed.
Assuming a Babel-valid expression is a working component
Syntax validation cannot resolve missing identifiers, imports, assets, hooks, types, CSS, server/client boundaries, or application-specific behavior. The destination build and tests remain authoritative.
Keeping javascript URLs or raw nested HTML
A preserved value is not an approved value. Replace active URLs with trusted navigation and treat srcDoc or raw HTML as a separate sanitization and Content Security Policy decision.
Turning legacy event strings into opaque inline callbacks
A generated arrow function can still rely on globals or DOM state. Move meaningful behavior into named functions with explicit inputs, error handling, and tests.
Checking appearance but not semantics
A visually similar result can lose labels, heading order, focus, keyboard controls, form validation, or error announcements. Compare the accessibility tree and interactions as well as screenshots.
FAQ
Why should I convert a fragment instead of the whole HTML file?
A component usually owns one task and should not contain document wrappers or unrelated metadata. Smaller fragments make parser changes, dependencies, behavior, and review scope observable.
Should generated event handlers be kept?
Use them only as migration evidence. Rewrite global calls, this references, return false behavior, and DOM queries into explicit React component logic, then test each interaction.
What should happen to inline style and style elements?
Inspect the converted values, then move stable rules into the destination project's CSS, module, or design-system approach. Keep inline objects for genuinely dynamic values rather than as an automatic default.
Can I use the output for untrusted CMS HTML?
Not as a security boundary. Conversion preserves source semantics and can retain active code or URLs. Untrusted content needs a separately defined sanitizer, URL policy, CSP, and rendering design.
How do I know the migration is complete?
The JSX parses, the project builds, lint and type checks pass, representative behavior matches the baseline, accessibility and mobile checks pass, network requests are expected, and every active-code warning has a documented resolution.