
React18 String Refs
Migrate legacy class components from deprecated React string refs to createRef/callback refs before React 19 constraints bite your codebase.
Overview
react18-string-refs is an agent skill for the Build phase that documents React class-component migrations from string refs to createRef with copy-paste before/after examples.
Install
npx skills add https://github.com/github/awesome-copilot --skill react18-string-refsWhat is this skill?
- Before/after pairs for single-DOM-element string refs with createRef and .current access
- Multiple named refs pattern: one createRef field per former string ref name
- Preserves class-component workflows where hooks are not yet adopted
- Shows focus(), value reads, and submit handlers updated to ref objects
- Structured sections for single ref and multiple refs in one component
- Documents single-DOM and multiple-ref migration patterns with paired before/after examples
Adoption & trust: 621 installs on skills.sh; 34.6k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You still have class components using ref="fieldName" and need a consistent, safe pattern to modernize refs without rewriting everything to hooks.
Who is it for?
Incremental React 18 upgrades on class-heavy codebases where one agent pass should standardize ref usage file by file.
Skip if: Greenfield function components where useRef is the default, or projects already fully on callback refs with no string refs left.
When should I use this skill?
When upgrading React 18+ codebases or editing class components that still use ref="name" string refs.
What do I get? / Deliverables
Agents produce updated class components that use createRef fields and .current for DOM access, ready for React 18+ and further ref callback upgrades if needed.
- Updated class components using React.createRef and ref={this.fieldRef}
- Consistent naming for one ref per former string ref identifier
Recommended Skills
Journey fit
Ref migration is hands-on UI work that happens while you are actively building or modernizing React surfaces, not during idea or launch phases. String-ref patterns live in component render trees and DOM focus/value access—canonical frontend implementation and refactor work.
How it compares
Use as a focused migration cookbook instead of generic “modernize React” chat that may miss multi-ref form cases.
Common Questions / FAQ
Who is react18-string-refs for?
Solo and indie builders maintaining React class components who need a clear, repeatable string-ref-to-createRef migration reference for agents or copilots.
When should I use react18-string-refs?
During Build (frontend) when upgrading React, auditing legacy forms and search boxes, or when lint/rules flag string refs; also handy in Ship (review) when reviewing ref-related PR diffs.
Is react18-string-refs safe to install?
It is documentation-only pattern guidance with no runtime installs; review the Security Audits panel on this Prism page before adding any skill to your agent workflow.
SKILL.md
READMESKILL.md - React18 String Refs
# String Refs - All Migration Patterns ## Single Ref on a DOM Element {#single-ref} The most common case - one ref to one DOM node. ```jsx // Before: class SearchBox extends React.Component { handleSearch() { const value = this.refs.searchInput.value; this.props.onSearch(value); } focusInput() { this.refs.searchInput.focus(); } render() { return ( <div> <input ref="searchInput" type="text" placeholder="Search..." /> <button onClick={() => this.handleSearch()}>Search</button> </div> ); } } ``` ```jsx // After: class SearchBox extends React.Component { searchInputRef = React.createRef(); handleSearch() { const value = this.searchInputRef.current.value; this.props.onSearch(value); } focusInput() { this.searchInputRef.current.focus(); } render() { return ( <div> <input ref={this.searchInputRef} type="text" placeholder="Search..." /> <button onClick={() => this.handleSearch()}>Search</button> </div> ); } } ``` --- ## Multiple Refs in One Component {#multiple-refs} Each string ref becomes its own named `createRef()` field. ```jsx // Before: class LoginForm extends React.Component { handleSubmit(e) { e.preventDefault(); const email = this.refs.emailField.value; const password = this.refs.passwordField.value; this.props.onSubmit({ email, password }); } render() { return ( <form onSubmit={this.handleSubmit}> <input ref="emailField" type="email" /> <input ref="passwordField" type="password" /> <button type="submit">Log in</button> </form> ); } } ``` ```jsx // After: class LoginForm extends React.Component { emailFieldRef = React.createRef(); passwordFieldRef = React.createRef(); handleSubmit(e) { e.preventDefault(); const email = this.emailFieldRef.current.value; const password = this.passwordFieldRef.current.value; this.props.onSubmit({ email, password }); } render() { return ( <form onSubmit={this.handleSubmit}> <input ref={this.emailFieldRef} type="email" /> <input ref={this.passwordFieldRef} type="password" /> <button type="submit">Log in</button> </form> ); } } ``` --- ## Refs in a List / Dynamic Refs {#list-refs} String refs in a map/loop - the most tricky case. Each item needs its own ref. ```jsx // Before: class TabPanel extends React.Component { focusTab(index) { this.refs[`tab_${index}`].focus(); } render() { return ( <div> {this.props.tabs.map((tab, i) => ( <button key={tab.id} ref={`tab_${i}`}> {tab.label} </button> ))} </div> ); } } ``` ```jsx // After - use a Map to store refs dynamically: class TabPanel extends React.Component { tabRefs = new Map(); getOrCreateRef(id) { if (!this.tabRefs.has(id)) { this.tabRefs.set(id, React.createRef()); } return this.tabRefs.get(id); } focusTab(index) { const tab = this.props.tabs[index]; this.tabRefs.get(tab.id)?.current?.focus(); } render() { return ( <div> {this.props.tabs.map((tab) => ( <button key={tab.id} ref={this.getOrCreateRef(tab.id)}> {tab.label} </button> ))} </div> ); } } ``` **Alternative - callback ref for lists (simpler):** ```jsx class TabPanel extends React.Component { tabRefs = {}; focusTab(index) { this.tabRefs[index]?.focus(); } render() { return ( <div> {this.props.tabs.map((tab, i) => ( <button key={tab.id} ref={el => { this.tabRefs[i] = el; }} // callback ref stores DOM node directly > {tab.label} </button> ))} </div> ); } } // Note: callback refs store the DOM node directly (not wrapped in .current) // this.tabRefs[i] is the element, not this.tabRefs[i].current ``` --- ## Ca