GraphSitter exposes several React and JSX-specific APIs for working with modern React codebases.

Key APIs include:

See React Modernization for tutorials and applications of the concepts described here

Detecting React Components with is_jsx

Codegen exposes a is_jsx property on both classes and functions, which can be used to check if a symbol is a React component.

# Check if a function is a React component
function = file.get_function("MyComponent")
is_component = function.is_jsx  # True for React components

# Check if a class is a React component
class_def = file.get_class("MyClassComponent")
is_component = class_def.is_jsx  # True for React class components

Working with JSX Elements

Given a React component, you can access its JSX elements using the jsx_elements property.

You can manipulate these elements by using the JSXElement and JSXProp APIs.

# Get all JSX elements in a component
for element in component.jsx_elements:
    # Access element name
    if element.name == "Button":
        # Wrap element in a div
        element.wrap("<div className='wrapper'>", "</div>")

    # Get specific prop
    specific_prop = element.get_prop("className")

    # Iterate over all props
    for prop in element.props:
        if prop.name == "className":
            # Set prop value
            prop.set_value('"my-classname"')

    # Modify element
    element.set_name("NewComponent")
    element.add_prop("newProp", "{value}")

    # Get child JSX elements
    child_elements = element.jsx_elements

    # Wrap element in a JSX expression (preserves whitespace)
    element.wrap("<div className='wrapper'>", "</div>")

Common React Operations

See React Modernization for more

Refactoring Components into Separate Files

Split React components into individual files:

# Find (named) React components
react_components = [
    func for func in codebase.functions
    if func.is_jsx and func.name is not None
]

# Filter out those that are not the default export
non_default_components = [
    comp for comp in react_components
    if not comp.export or not comp.export.is_default_export()
]

# Move these non-default components to new files
for component in react_components:
    if component != default_component:
        # Create new file
        new_file_path = '/'.join(component.filepath.split('/')[:-1]) + f"{component.name}.tsx"
        new_file = codebase.create_file(new_file_path)

        # Move component and update imports
        component.move_to_file(new_file, strategy="add_back_edge")

See Moving Symbols for more details on moving symbols between files.

Updating Component Names and Props

Replace components throughout the codebase with prop updates:

# Find target component
new_component = codebase.get_symbol("NewComponent")

for function in codebase.functions:
    if function.is_jsx:
        # Update JSX elements
        for element in function.jsx_elements:
            if element.name == "OldComponent":
                # Update name
                element.set_name("NewComponent")

                # Edit props
                needs_clsx = not file.has_import("clsx")
                for prop in element.props:
                    if prop.name == "className":
                        prop.set_value('clsx("new-classname")')
                        needs_clsx = True
                    elif prop.name == "onClick":
                        prop.set_name('handleClick')

                # Add import if needed
                if needs_clsx:
                    file.add_import_from_import_source("import clsx from 'clsx'")

                # Add import if needed
                if not file.has_import("NewComponent"):
                    file.add_symbol_import(new_component)

Was this page helpful?