Look for a shared responsibility

Two pieces of UI can look similar while serving different interactions. Extracting them solely because their markup shares a border and a heading can create a component with many contradictory options. Start by asking what responsibility is truly repeated: presenting a project summary, collecting a labeled value or confirming an action. A useful abstraction gives that responsibility a clear name.

Write representative usages first

For a project summary component, sketch one usage with an image, one without an image and one with a long title. Decide which data is required and how navigation works. These examples expose whether the proposed API describes the feature naturally. If every call requires several exception flags, the common boundary may be smaller than the first design suggested.

Specify state ownership

A component can be controlled by its parent, own a local draft, or combine the two through a documented editing lifecycle. Avoid unclear hybrids where the parent supplies a value but the child also changes its private copy without reporting the change. Name callbacks after the intent they communicate, and include enough information that the parent does not need to inspect the component’s internal DOM.

Make accessibility part of the contract

A field wrapper must preserve labels, names, descriptions and useful native attributes. A disclosure must expose expansion state and keyboard behavior. These should not be optional extras added separately at each call site. Explain which responsibilities the component guarantees and which the caller must supply, such as meaningful link text or an appropriate alternative description for an image.

Keep variations finite and meaningful

Variants can express established design choices, such as compact and comfortable density. They should not become a replacement for every possible CSS declaration. Prefer composition when content structure genuinely changes, and preserve an escape hatch only when its cost is understood. A small explicit API is easier to maintain than a generic object that silently passes arbitrary behavior throughout the tree.

Verify the abstraction at its boundaries

Render realistic long content, empty optional content, disabled actions and multiple instances. Check events, focus movement and updates from the parent. Review whether a caller can create an invalid combination and whether that combination should be rejected by types or runtime validation. Reuse pays off when the component makes correct behavior easier across real screens, rather than merely reducing the number of repeated lines.

Further reading

Put the idea to work.

Explore free browser tools for images, text and developer workflows.

Try a free tool