Appendix A
Appendix A - Front-End Architectural Rosetta Stone
Vanilla JavaScript, React, and Vue Compared by Architectural Concept
This appendix is a translation guide.
Its purpose is not to teach three separate technologies.
Instead, it takes the architectural ideas used throughout this book and shows how the same responsibility is commonly expressed in:
- browser-platform / Vanilla JavaScript;
- React;
- Vue.
The key word is responsibility.
The implementations are not identical.
For example:
- React primarily expresses UI as repeated render calculations followed by reconciliation and commit;
- Vue tracks reactive dependencies and updates affected rendering/effects;
- browser-platform JavaScript gives you lower-level DOM, events, classes, modules, Custom Elements, and Web APIs from which you build your own update model.
Therefore, this appendix should not be read as:
It should be read as:
If I understand the architectural responsibility, what is the closest normal way to express it in each environment?
That makes the appendix useful when:
- moving between React and Vue;
- reading an unfamiliar codebase;
- deciding whether a framework abstraction is actually necessary;
- translating a design pattern without copying framework-specific syntax;
- teaching frontend architecture independently of one library.
1. Quick Rosetta Stone
| Architectural responsibility | Browser / Vanilla JavaScript | React | Vue |
|---|---|---|---|
| UI unit | function/class/Custom Element/module | component | component |
| External input | function args, properties, attributes | props | props |
| Output to parent | callback, DOM CustomEvent | callback prop | emitted component event |
| Nested UI composition | DOM nodes, callbacks, templates | children / render props | slots / scoped slots |
| Local state | variables/objects + explicit update/render logic | useState, useReducer | ref, reactive |
| Derived value | function/getter | calculate during render, optionally useMemo | computed |
| Reaction to external system | event/subscription/lifecycle code | useEffect | watch, watchEffect, lifecycle hooks |
| Direct DOM reference | DOM query/reference | useRef | template ref |
| Deep dependency sharing | module/service/object reference | Context | provide / inject |
| Reusable stateful behavior | function/class/module | custom Hook | composable |
| Conditional rendering | DOM creation/removal | JavaScript condition in JSX | v-if, v-show |
| List rendering | loops + DOM creation | map() + key | v-for + :key |
| Controlled input | assign value + handle input event | value + onChange | v-model or :value + @input |
| Uncontrolled input | browser owns current value | defaultValue + ref/FormData | native DOM/form behavior or template ref |
| Lifecycle setup | explicit initialization | Effect/lifecycle abstraction | lifecycle hooks |
| Lifecycle cleanup | remove listener/cancel/close | Effect cleanup | onUnmounted, watcher cleanup |
| DOM event | addEventListener | JSX event prop | v-on / @event |
| Shared state | shared object/module/custom store | lift state / Context / store | lift state / provide-inject / store |
| URL state | URL, URLSearchParams, History API | router/framework APIs over URL/history | Vue Router APIs over URL/history |
| Async module loading | import() | import(), framework lazy APIs | import(), async component/router APIs |
| Network request | fetch() | fetch() / framework/data library | fetch() / framework/data library |
| Escape hatch to platform | already at platform level | refs, Effects, DOM APIs | template refs, lifecycle/watchers, DOM APIs |
The table is intentionally compact.
The rest of this appendix explains the important differences behind it.
2. Component: What Is the Unit of UI?
Architectural responsibility
A component should own a coherent piece of interface responsibility.
It may define:
- structure;
- inputs;
- output events;
- local state;
- behavior;
- lifecycle.
The architectural question is:
What belongs together, and what deserves its own boundary?
The answer should come before framework syntax.
Browser / Vanilla JavaScript
There is no single mandatory component model.
A component can be represented by:
- a function returning DOM;
- a class;
- a factory;
- a Custom Element;
- a module that mounts/unmounts a region.
A simple function-based component:
Use:
The function creates one coherent UI unit.
React
A component is normally a function returning JSX.
React calls the component during rendering.
The returned JSX describes desired UI.
Vue
A Vue Single-File Component often separates script and template while remaining one component boundary.
Vue connects the template to reactive component state and props.
Translation principle
A component is an architectural boundary.
React and Vue provide standardized component runtimes.
Vanilla JavaScript makes you define more of that runtime behavior yourself.
3. Inputs: Properties Passed Into a Component
Architectural responsibility
Components need external information.
Examples:
A healthy component input API should be:
- understandable;
- narrow;
- stable;
- explicit.
Vanilla JavaScript
Function arguments:
Custom Element properties:
Custom Element attributes for serializable markup-facing configuration:
Remember:
are related but not identical concepts.
React
Props:
Inside:
React props are read-only inputs for a render.
Vue
Props:
Declaration:
Vue props follow a one-way-down data flow.
Children should normally request changes rather than mutate parent-owned values.
4. Outputs: How a Child Communicates Upward
A child often needs to say:
The architectural principle is:
The child reports intent; the owner decides what state changes.
5. Callback Output
Vanilla JavaScript
React
Callback props are a normal React communication pattern.
Vue
Vue can receive callback props too, but framework-native component communication commonly uses emitted events.
Parent:
6. DOM Events vs Component Events
These should not be confused.
DOM:
React:
Vue:
These represent browser interaction.
Component-level communication is conceptually higher-level:
A good component API often communicates domain intent rather than exposing every internal DOM event.
7. Custom Events in Vanilla Components
A Custom Element can emit a DOM CustomEvent.
Consumer:
Architecturally, this is close to Vue component emits.
React usually expresses the same parent-child contract through callback props rather than DOM custom events.
8. Composition: Passing Interface Structure
Components should not need a prop for every possible layout variation.
Composition lets the parent provide child content or behavior.
9. Vanilla Composition
A function can accept DOM content:
Custom Elements can use native slots:
Shadow DOM template:
Native Web Component slots and Vue slots are related composition ideas, though their runtimes differ.
10. React Composition
React uses children.
Use:
Multiple composition regions are commonly modeled as props:
11. Vue Composition
Default slot:
Use:
Named slots:
12. Children and Slots Solve the Same Architectural Problem
The problem is:
The container should own layout/behavior without knowing every concrete nested UI element.
React normally calls this:
Vue normally calls this:
Web Components use:
as a browser primitive.
Do not translate syntax mechanically.
Translate the responsibility.
13. Scoped Composition
Sometimes the container needs to expose data to parent-provided content.
Architectural idea:
React may use a render prop:
Vue may use a scoped slot:
Vanilla code may use a callback:
This is one of the clearest Rosetta Stone mappings.
14. Local State
Architectural responsibility
Local state belongs to one UI boundary.
Examples:
15. Vanilla Local State
You choose the update mechanism.
Here:
16. React Local State
React state update:
React recalculates component output.
17. Vue Local State
Vue tracks the reactive count value used by the template.
Changing it causes dependent UI work.
18. Local State Translation
The user-visible responsibility is the same.
The runtime mechanism is not.
19. ref Means Different Things in React and Vue
This is an important terminology trap.
React:
commonly represents a mutable reference that does not itself trigger rendering when .current changes.
Vue:
normally creates a reactive value.
These concepts are not equivalent despite sharing the word ref.
Vue also has template refs, which are much closer to React DOM refs.
20. Object State
Vanilla
You decide whether mutations require rendering.
React
Updates usually create a new value:
Identity matters to React state/update patterns.
Vue
Then:
Vue observes reactive property access/mutation.
Again, the programming models differ.
21. Derived State
Suppose:
Usually do not store all three.
Store:
derive:
This architectural rule is framework-independent.
22. Vanilla Derived Value
or:
23. React Derived Value
Usually calculate during render.
If the calculation is genuinely expensive and repeated unnecessarily, memoization may be appropriate:
Do not use an Effect merely to copy derived data into state.
24. Vue Derived Value
For simple expressions, the template can calculate directly.
For reusable/cached reactive derivation:
Vue tracks dependencies automatically.
25. Derived-State Rosetta Stone
| Need | Vanilla | React | Vue |
|---|---|---|---|
| Cheap derived value | function/getter | calculate during render | expression/function |
| Cached reactive derivation | custom memoization | useMemo when justified | computed |
| Store duplicate derived value? | usually no | usually no | usually no |
The principle is more important than the API.
26. Effects: Synchronizing with Something Outside Normal Rendering
An effect exists when UI state must synchronize with something external.
Examples:
- browser event listener;
- network subscription;
- third-party widget;
- media element;
- timer;
- WebSocket.
Effects should not become a general-purpose “run code when something changes” mechanism for ordinary derivation.
27. Vanilla Effect
The returned function is cleanup.
28. React Effect
Architectural meaning:
React Effects should generally not be used merely to calculate render data.
29. Vue Watcher / Lifecycle Effect
One possibility:
For state-change-triggered side effects:
or:
30. Effect Translation Warning
Do not mechanically translate:
They overlap conceptually but belong to different reactivity/rendering models.
Ask instead:
What external system or side effect am I synchronizing?
Then choose the most natural mechanism.
31. Event Handler vs Effect
This distinction is architectural.
User presses:
The purchase request is caused by that event.
Put it in the event path.
Do not create:
unless architecture truly requires indirection.
This principle holds across Vanilla, React, and Vue.
32. Lifecycle Setup and Cleanup
A component may acquire resources:
It must release them.
Vanilla
Explicit mount/unmount:
Custom Elements:
React
Effect setup + returned cleanup:
Vue
Lifecycle hooks:
Watchers/effects also support cleanup mechanisms for invalidated async work.
33. Direct DOM Access
Declarative frameworks reduce direct DOM manipulation.
They do not eliminate it.
Legitimate examples:
- focus;
- measurement;
- third-party library integration;
- media control.
34. Vanilla DOM Reference
You already have direct platform access.
Prefer keeping references when you already created the element rather than repeatedly querying.
35. React Ref
A ref is an escape hatch to a rendered DOM node or other mutable value.
36. Vue Template Ref
The exact helper syntax can vary with Vue version/style, but the architectural idea is:
37. Do Not Use DOM References for Ordinary Data Flow
Weak architecture:
Stronger architecture:
Refs should remain escape hatches.
38. Conditional Rendering
Vanilla
Or show/hide an existing node:
React
or:
Vue
For visibility without removing the element:
39. Render vs Hide
Architectural distinction:
vs:
This affects:
- lifecycle;
- state preservation;
- accessibility;
- performance.
Choose based on behavior, not syntax preference.
40. List Rendering
Vanilla
If updating in place, you need your own identity strategy.
React
The key helps React reason about item identity across renders.
Vue
Keys similarly express identity across list updates.
41. Key Means Identity, Not “Silence the Warning”
Good:
Risky:
when ordering/insertion changes.
The architectural question is:
What makes this logical item the same item across updates?
That question applies beyond frameworks.
42. Controlled and Uncontrolled Components
A controlled component receives its important state from an owner.
An uncontrolled component owns more of that state internally.
The terms are common in React but the architectural idea is universal.
43. Controlled Toggle - Vanilla
The caller owns:
and tells the view what to display.
44. Controlled Toggle - React
State lives in the parent.
45. Controlled Toggle - Vue
Parent can use:
46. Uncontrolled Component
The component/browser owns state.
Example:
may remain in the DOM until submission.
Vanilla:
React uncontrolled input:
Then read through form submission/ref.
Vue often encourages reactive form bindings with v-model, but native form behavior remains available.
The right choice depends on ownership requirements.
47. Form Inputs
Vanilla
or use native form submission:
React
Controlled:
Uncontrolled:
Vue
Conceptually, v-model connects:
It is convenient syntax over a controlled synchronization pattern.
48. Form Architecture Does Not Reduce to Binding Syntax
For complex forms, the important questions remain:
- who owns values?
- what is dirty?
- what is touched?
- where is validation?
- what is server authority?
- how is draft state preserved?
React and Vue syntax differ.
The architecture does not.
49. Lifting State Up
When two siblings need coordinated state:
move ownership to their closest sensible common owner.
Vanilla
A parent/controller object may own:
and call both child update functions.
React
Parent owns state:
Vue
Parent owns reactive state:
Template:
50. Single Source of Truth
The principle is:
Not:
A large application can have hundreds of local sources of truth, each for a different concern.
51. Dependency Sharing Through a Tree
Sometimes a deeply nested component needs:
Passing the same prop through every intermediate component can be noisy.
52. Vanilla Dependency Sharing
Options include:
- module import;
- closure;
- service object;
- Custom Element property;
- DOM event;
- explicit dependency injection container.
Example:
Explicit dependency passing is often preferable to global singletons.
53. React Context
Create:
Provide:
A descendant can read the nearest provided value through the React Context API.
Context is useful for tree-wide dependencies.
It is not automatically a replacement for all state management.
54. Vue Provide / Inject
Provider:
Descendant:
For larger applications/libraries, Symbol keys help avoid collisions.
Vue’s model explicitly resembles dependency injection.
55. Context and Provide/Inject Are Closest Analogues
Both solve:
But do not overuse them.
Explicit props remain valuable when dependencies are part of the immediate component API.
56. Dependency Injection Is Not Global State
Good injected dependency:
Potentially poor injected dependency:
The pattern should reduce plumbing without hiding ownership.
57. Reusable Stateful Behavior
Sometimes several components need the same behavior without sharing the same rendered UI.
Examples:
- online status;
- debounced value;
- resize observer;
- API query logic.
58. Vanilla Reusable Behavior
A normal function or class often suffices.
59. React Custom Hook
A custom Hook packages reusable React stateful behavior.
60. Vue Composable
A composable packages reusable Vue reactive/lifecycle behavior.
61. Custom Hook vs Composable
Closest conceptual mapping:
Both can:
- package framework-aware state;
- package lifecycle;
- package derived values;
- package subscriptions.
They are not interchangeable source code.
They share an architectural purpose.
62. Pure Utility vs Hook/Composable
If code needs no framework reactivity/lifecycle:
keep it a normal function.
Do not turn every helper into:
or:
Pure domain logic remains easier to test and reuse.
63. Refs vs State
Use state/reactivity for values that should affect rendered UI.
Use mutable references for values that must persist but do not themselves require a render.
React
State:
Mutable reference:
Changing:
does not by itself request a render.
Vue
Reactive state:
For non-reactive mutable data, use ordinary JavaScript variables/objects where appropriate.
A template ref is specifically for a DOM/component reference.
The terminology differs, so translate by behavior rather than name.
64. State Machine Thinking
A workflow may have explicit states:
This can be expressed in every environment.
Vanilla:
React:
Vue:
The architecture is the state transition model.
Framework APIs are storage mechanisms.
65. Reducer Pattern
A reducer expresses:
Pure function:
This function is framework-neutral.
React has:
as a built-in component-state mechanism around reducer logic.
Vue can use the same reducer function inside reactive state management, but does not require one canonical reducer API for local components.
66. Routing
Routing is not fundamentally a framework feature.
It maps:
67. Browser Routing Primitives
Platform tools include:
Example:
Navigation:
Then application code must render the new route and handle Back/Forward.
68. React Routing
React itself does not define one complete application router.
React applications commonly use:
- a routing library;
- a React framework with integrated routing.
The architectural concepts remain:
Do not confuse a particular router’s API with routing itself.
69. Vue Routing
Vue’s official ecosystem commonly uses Vue Router.
It maps:
into Vue components/reactivity.
Again, the platform URL remains the underlying browser contract.
70. URL State
Same architectural rule in all three:
Use URL state when it should be:
- reload-safe;
- shareable;
- navigable through Back/Forward;
- bookmarkable.
Examples:
71. URL State Example
Desired URL:
Vanilla:
React/Vue routers expose convenience APIs around the same URL state.
The architecture should remain understandable without the convenience layer.
72. Navigation Is Not Just Rendered Component State
Weak:
with no URL change.
Now:
- Back may fail;
- refresh may lose state;
- sharing may fail.
If the state represents navigation, use the browser navigation model.
73. Async Data Fetching
All three ultimately depend on browser/network primitives.
The architectural questions are:
- who starts the request?
- who owns loading/error state?
- is data cached?
- how is stale data handled?
- how is cancellation handled?
- does the framework/server prefetch it?
74. Vanilla Fetch
Then your own controller/view code manages state.
75. React Fetching
A simple component can fetch through an Effect.
But modern React applications often use:
- router loaders;
- framework server/data APIs;
- server-state libraries;
- server components/framework data mechanisms.
The architectural rule is:
Avoid creating ad hoc request synchronization in every component when the application has a better data boundary.
76. Vue Fetching
A component can fetch in lifecycle code or a watcher.
Larger applications often use:
- route-level loading conventions;
- framework data APIs;
- server-state libraries;
- composables.
Again, architecture should centralize repeated request policy.
77. Do Not Translate Fetching as useEffect ↔ watch
Those are low-level mechanisms.
The stronger translation is:
Possible answer:
The same question applies in React and Vue.
78. Loading State
Vanilla:
React:
Vue:
Syntax differs.
State modeling remains the important part.
79. Error State
Represent expected operational failure explicitly.
Avoid:
meaning both:
and:
State semantics should remain precise in every framework.
80. Cancellation
Platform primitive:
React/Vue architecture determines where the controller belongs and when cleanup occurs.
The cancellation mechanism itself is browser-standard.
81. Debounced Search
The pattern:
is independent of framework.
React may package it into a Hook.
Vue may package it into a composable.
Vanilla may package it into a controller/module.
The user problem is the same.
82. Dynamic Imports
Browser platform:
This is standardized JavaScript.
Frameworks build route/component lazy-loading APIs on top of it.
83. React Lazy Boundary
A React environment may use framework-specific lazy route/component capabilities.
Underlying idea:
Do not memorize one lazy API as the architectural concept.
84. Vue Async Component
Vue provides framework-level async component capabilities, and routers/frameworks can lazy-load route components.
Again:
is the underlying pattern.
85. Direct DOM Events
Vanilla
React
Vue
All eventually represent browser interaction.
Frameworks normalize integration into their component models.
86. Event Delegation
Vanilla:
React and Vue framework runtimes manage event integration, but event delegation as an application technique may still be useful in direct DOM/custom integration.
Do not assume framework event syntax means browser event propagation disappeared.
87. Preventing Default Behavior
Vanilla:
React:
Vue can call it directly or use a template event modifier:
The browser concept is still:
88. Composition Over Inheritance
All three environments generally benefit from composing smaller responsibilities rather than building deep UI inheritance hierarchies.
Vanilla:
React:
Vue:
Inheritance can exist in JavaScript.
It is rarely the primary UI composition strategy.
89. Wrapper Component
Architectural responsibility:
Vanilla:
React:
Vue:
90. Headless Behavior
A headless abstraction owns:
while consumer owns:
Possible forms:
Vanilla:
React:
Vue:
The pattern is architectural.
Not tied to one framework.
91. Compound Components
Compound components expose several coordinated subcomponents.
Conceptual API:
React can implement coordination through Context.
Vue can implement it through provide/inject.
Web Components can coordinate through DOM relationships, events, and properties.
The risk in every environment is hidden coupling.
Compound components should represent a genuinely cohesive widget.
92. Service / Dependency Object
Some dependencies are not UI.
Example:
This can remain plain JavaScript and be used from React or Vue.
Do not wrap every service inside framework state unless its lifecycle/reactivity requires it.
93. Domain Logic Should Stay Framework-Light
Example:
This belongs to domain logic.
Use from React:
Use from Vue:
The business rule itself remains framework-neutral.
94. Runtime Validation
Chapter 5’s trust-boundary model remains identical.
React and Vue do not change this requirement.
Framework typing is not runtime validation.
95. Component API Design
Same design questions:
React answers through:
Vue answers through:
Vanilla answers through:
96. Reusable vs Application-Specific
Shared:
Application-specific:
This decision should not change merely because React makes components easy to create or Vue makes SFCs convenient.
Reuse is architectural.
97. State Store Escalation
A practical escalation path:
Do not jump directly to the right side.
At each step ask:
Is the sharing scope genuinely this large?
98. Vanilla Shared Store
A tiny observable store can be built directly:
The interesting part is not the 25 lines.
It is the policy around:
- ownership;
- updates;
- subscriptions;
- persistence;
- debugging.
That is why mature state libraries exist.
99. React External Store
React can integrate external stores through dedicated subscription patterns/APIs.
The architecture still separates:
from:
Do not assume a store must be built from Context alone.
100. Vue Store
Vue reactive primitives can support shared state, and larger applications may use a dedicated store library.
Again, the architecture should justify:
before selecting the tool.
101. Memoization
Memoization caches previous computation.
Vanilla:
React:
Vue:
These are not direct equivalents.
Do not translate:
without understanding why each exists.
102. Memoization Is an Optimization, Not State Architecture
Do not use memoization to repair:
- wrong ownership;
- giant components;
- unnecessary global updates.
First reduce unnecessary work structurally.
Then optimize measured remaining work.
103. Component Identity
Every framework/runtime needs some notion of:
React exposes this strongly through:
- component position;
- type;
- keys.
Vue also uses component/VNode identity and keys.
Vanilla code must manage DOM identity directly if it updates existing nodes rather than rebuilding.
Identity affects:
- state preservation;
- reset;
- list updates.
104. Resetting UI State
Sometimes the desired behavior is:
React often uses a changed key to establish new identity.
Vue can also use key to force replacement/recreation behavior.
Vanilla code explicitly destroys old UI and constructs new UI.
The architectural question is:
Is this the same logical instance or a new one?
105. Template / JSX / DOM Construction
These are authoring mechanisms.
Vanilla:
or template cloning.
React:
Vue:
with template compiler/runtime behavior.
Do not confuse syntax convenience with architectural responsibility.
106. Styling Boundary
Vanilla:
React:
Vue:
Styling architecture from Chapter 3 remains independent of component framework.
107. CSS Custom Properties Work Everywhere
Vanilla, React, and Vue components can consume the same tokens.
This is a good example of a framework-neutral architectural layer.
108. Accessibility Semantics Work Everywhere
Correct:
is correct whether created through:
- DOM API;
- JSX;
- Vue template.
Frameworks do not replace semantic HTML.
109. Internationalization Works Across Frameworks
Platform:
React can call it during rendering.
Vue can call it inside computed/render logic.
The Intl capability remains platform-level.
110. Directionality Is HTML/CSS, Not a Framework Feature
Logical CSS:
React/Vue do not change these fundamentals.
111. Browser Storage
Platform APIs:
React/Vue merely decide how stored values enter their reactivity/rendering models.
Storage architecture should remain independent from framework state when possible.
112. Persistent State Is Not Automatically UI State
Example:
may have:
Do not treat localStorage itself as the reactive store.
Read, validate, migrate, then synchronize intentionally.
113. WebSocket / SSE
Platform APIs:
Framework integration:
Vanilla:
React:
Vue:
The transport remains platform-level.
114. Service Worker
Service Worker lives outside the component runtime.
It is not:
or:
It is a browser worker lifecycle controlling requests/cache/offline behavior.
Applications communicate with it through browser APIs.
Frameworks are consumers.
115. Error Boundary
This concept has different framework support.
React provides error-boundary mechanisms in its rendering model/framework ecosystem.
Vue provides application/component error handling hooks.
Vanilla code uses:
try/catch;- Promise rejection handling;
- explicit component/controller fallback logic;
- global browser error events where appropriate.
Architecturally:
Contain failure close to the feature when possible.
Do not assume all frameworks expose identical error-containment semantics.
116. Suspense / Loading Boundaries
Frameworks may provide special primitives for coordinating async rendering/loading boundaries.
There is no direct one-to-one Vanilla DOM API equivalent.
The platform primitives are lower-level:
This is an example where Rosetta Stone mapping becomes approximate.
When a framework provides a higher-level scheduling/rendering feature, compare the architectural goal rather than searching for matching syntax.
117. Portals / Teleport
Sometimes UI should be owned by one component but rendered elsewhere in the DOM.
Example:
Vanilla:
React:
Vue:
Architectural responsibility:
118. Transition / Animation Integration
Platform:
React and Vue may provide helpers around enter/leave lifecycle.
Prefer platform animation primitives where sufficient.
Framework helpers are useful for coordinating component mount/unmount with those primitives.
119. Controlled Side Effects
A good architecture makes side effects explicit.
Examples:
Keep them at clear boundaries.
Pure render/domain logic becomes easier to test and reason about.
120. Testing Rosetta Stone
| Test responsibility | Vanilla | React | Vue |
|---|---|---|---|
| Pure domain logic | normal unit test | same | same |
| DOM behavior | DOM/browser test | component test | component test |
| Accessible semantics | role/name/label queries | role/name/label queries | role/name/label queries |
| Network boundary | intercept/mock request | same | same |
| E2E browser flow | browser automation | same | same |
Testing behavior should remain framework-light.
A test for:
should not care whether the button came from JSX or a Vue template.
121. Testing User Semantics
Prefer:
over:
This is particularly valuable in a Rosetta Stone context because user semantics survive framework changes.
122. Design System Rosetta Stone
A design system can expose the same conceptual Button API:
Implementation variants:
Tokens can remain shared.
This suggests an important architecture:
But maintaining three component implementations has cost.
Only do this if products genuinely need it.
123. Web Components as Framework-Neutral Integration
A Custom Element can be used from:
- plain HTML;
- React;
- Vue;
- other frameworks.
This can be useful for organization-wide widgets or embedding boundaries.
But Web Components do not automatically solve:
- state management;
- routing;
- server rendering;
- application architecture.
Use them where browser-level component interoperability is valuable.
124. Framework Wrapper Around Web Component
A React/Vue wrapper may improve:
- typing;
- event integration;
- framework conventions.
Architecture:
This can be cleaner than rewriting the same complex widget several times.
But wrapping every trivial component may be unnecessary.
125. Server Rendering Translation
Vanilla/server templates
Server returns HTML directly.
React ecosystem
React frameworks can render component trees on the server and hydrate or use server-oriented component models.
Vue ecosystem
Vue/Nuxt can server-render Vue component trees and hydrate on the client.
Architectural comparison:
Do not reduce SSR comparison to component syntax.
126. Static Generation Translation
All three can produce static HTML.
Vanilla:
React:
Vue:
Static generation is a deployment/render topology.
Not a React or Vue feature by definition.
127. Client-Side Rendering Translation
All three can render UI after JavaScript executes.
Vanilla:
React:
Vue:
The performance and accessibility implications depend on implementation.
128. Progressive Enhancement Translation
Vanilla:
React/Vue:
possible when framework architecture preserves functional server/native baseline, especially through framework/server integration.
Do not assume:
Modern frameworks support multiple rendering topologies.
129. Package Boundary Translation
A shared package can contain:
The architectural question is:
Who is allowed to depend on what?
The package manager does not care whether the code is React or Vue.
Dependency direction remains architecture.
130. Event Bus
Vanilla can use:
React/Vue can also connect to event emitters.
But a global event bus can hide ownership in every environment.
Prefer:
- explicit callbacks;
- state owner;
- store;
- URL/server state
unless decoupled broadcasting is genuinely required.
131. Dependency Injection Rosetta Stone
| Responsibility | Vanilla | React | Vue |
|---|---|---|---|
| Explicit dependency | function arg / constructor arg | prop | prop |
| Deep tree dependency | service/module/DI | Context | provide/inject |
| App-wide infrastructure | module/service/container | root provider | app-level provide/plugin |
| Mutations owned by provider | explicit methods | provider actions/callbacks | provided mutation function |
The stable principle is:
Keep mutation responsibility near the owner of the state/dependency.
132. Event Output Rosetta Stone
| Scenario | Vanilla | React | Vue |
|---|---|---|---|
| Native button click | addEventListener("click") | onClick | @click |
| Child says “save” | callback or CustomEvent | onSave callback prop | emit("save") |
| App-wide broadcast | EventTarget/store | store/context/event emitter | store/provide-inject/event emitter |
| Preferred for parent-child domain intent | callback / custom event | callback | component emit |
No mechanism should be chosen merely because it is available.
133. Composition Rosetta Stone
| Need | Vanilla | React | Vue |
|---|---|---|---|
| Default nested content | append child nodes | children | default slot |
| Named regions | explicit parameters / native slots | named props | named slots |
| Parent-controlled rendering with child data | callback | render prop | scoped slot |
| Reusable stateful behavior without UI | module/controller | custom Hook | composable |
134. State Rosetta Stone
| State kind | Vanilla | React | Vue |
|---|---|---|---|
| Local UI | variable/object + render | useState | ref/reactive |
| Complex transitions | reducer/store | useReducer/store | reducer-style function/store |
| Derived | function/getter | render calculation / useMemo | computed |
| Tree dependency | object/service | Context | provide/inject |
| Server state | custom cache | data/router/query layer | data/router/query layer |
| URL state | History/URL APIs | router APIs | router APIs |
| Persistent | storage API | storage + state integration | storage + reactivity integration |
135. Side-Effect Rosetta Stone
| Side effect | Vanilla | React | Vue |
|---|---|---|---|
| DOM listener | setup manually | Effect | lifecycle/composable |
| Subscription | subscribe/unsubscribe | Effect/external-store abstraction | lifecycle/watch/composable |
| User-triggered POST | event handler | event handler | event handler |
| Derived display value | function | render calculation | computed |
| Watch one reactive value to call external API | custom subscription | Effect if appropriate / data layer | watch or data layer |
| Cleanup | explicit | Effect return | unmount/watcher cleanup |
This table is especially important because misuse of effect mechanisms is a common source of complexity.
136. Framework Translation Anti-Patterns
Anti-pattern 1 - Translating API names instead of responsibilities
Bad question:
Better:
Anti-pattern 2 - Rebuilding one framework inside another
A React developer moving to Vue may try to:
- make every value immutable;
- manually memoize everything;
- reproduce Hook structure mechanically.
A Vue developer moving to React may try to:
- mutate reactive-looking objects directly;
- expect automatic dependency tracking;
- reproduce watchers for ordinary derivation.
Learn the target runtime’s model.
Preserve architecture, not implementation habits.
Anti-pattern 3 - Treating Vanilla as “no architecture”
Vanilla code still needs:
- ownership;
- components/modules;
- lifecycle;
- cleanup;
- state boundaries.
A framework supplies conventions.
Without a framework, you must supply them deliberately.
Anti-pattern 4 - Treating framework convenience as browser capability
Examples:
are framework abstractions.
Examples:
are browser/platform capabilities.
Know which layer owns the concept.
137. Comparative Example - Search Panel
Let us implement the same responsibility three ways.
Requirements:
- search textbox;
- local query state;
- submit event;
- parent performs search;
- clear button.
This is intentionally small.
138. Vanilla Search Panel
Architecture:
139. React Search Panel
Architecture is unchanged.
140. Vue Search Panel
Same architecture:
141. What the Search Example Teaches
Do not memorize:
Notice the deeper invariants:
Those concepts survive framework migration.
142. Comparative Example - Derived Filtered List
Requirements:
Do not store visibleProducts separately unless there is a strong reason.
Vanilla
Call when rendering/updating.
React
If proven expensive:
Vue
The architectural principle is:
143. Comparative Example - External Subscription
Requirement:
Source:
The external system is the browser.
Vanilla
React
Package the subscription in a Hook or use the appropriate external-store abstraction.
The important structure:
not the exact API.
Vue
Package it in a composable:
Again, the architecture translates.
144. Comparative Example - Deep Locale Dependency
Requirement:
Bad architecture:
Potential approaches:
Vanilla:
React:
Vue:
But if only one direct child needs locale:
is still clearer.
145. Comparative Example - Dialog
Architectural responsibilities:
- open state;
- accessible label;
- focus;
- Escape handling;
- backdrop;
- close intent;
- portal/teleport/body placement if needed.
The framework syntax is secondary.
A design-system Dialog should provide the same behavioral contract whichever implementation technology is used.
146. Comparative Example - Shared Product Query
Requirement:
all need Product P-42.
Possible architecture:
not:
React and Vue may use different query libraries or framework loaders.
Vanilla may use a shared request cache.
The architecture is:
147. When to Stay in Vanilla
Vanilla/platform code is especially strong when:
- behavior is small;
- DOM is mostly static;
- long framework lifecycle is unnecessary;
- interoperability matters;
- the platform already solves the problem.
Examples:
148. When a Component Framework Helps
A framework becomes useful when the UI has substantial:
- state-driven rendering;
- composition;
- repeated component patterns;
- lifecycle;
- team conventions;
- ecosystem needs.
The decision is not:
Both can be engineered well.
The question is whether the runtime/conventions reduce total complexity.
149. React Mental Translation
When reading React, think:
This is more useful than memorizing Hooks independently.
150. Vue Mental Translation
When reading Vue, think:
151. Vanilla Mental Translation
When reading framework code from a platform perspective, ask:
This prevents framework abstractions from becoming magic.
152. Migration Thinking - React to Vue
Preserve:
Translate:
Do not try to reproduce React’s render/effect semantics exactly.
153. Migration Thinking - Vue to React
Preserve:
Translate:
Do not expect automatic dependency tracking in ordinary React code.
154. Migration Thinking - Framework to Vanilla
Do not simply delete framework APIs one by one.
First identify:
Then choose platform structures:
A framework replacement requires recreating the necessary runtime conventions.
155. Migration Thinking - Vanilla to Framework
Do not wrap every existing function as a component.
Keep:
as normal modules.
Move UI lifecycle/state responsibilities into framework components gradually.
This keeps architecture cleaner.
156. Framework-Neutral Layers
These often remain reusable across React and Vue:
This is one reason separating domain logic from UI runtime is valuable.
157. Framework-Specific Layers
These often need dedicated implementations:
Do not spend excessive effort pretending these are framework-neutral.
Some coupling is legitimate.
158. Browser-Platform Layer
Always underneath:
React and Vue organize how you use the platform.
They do not replace it.
159. A Layered Architecture That Survives Framework Change
The more stable lower layers remain ordinary platform/TypeScript code, the easier framework evolution becomes.
But do not add artificial abstraction merely to claim portability.
160. Final Comparative Checklist
When you encounter unfamiliar code, ask these questions.
Components
Inputs
Outputs
State
Derived values
Effects
Composition
Dependencies
URL
Server data
Lifecycle
DOM escape hatches
These questions translate much better than API names.
161. Compact Translation Dictionary
props
React:
Vue:
Vanilla:
Parent callback
React:
Vue:
Vanilla:
children
React:
Vue:
Vanilla:
Local reactive value
React:
Vue:
Vanilla:
Derived reactive value
React:
Vue:
Vanilla:
External synchronization
React:
Vue:
Vanilla:
DOM reference
React:
Vue:
Vanilla:
Deep tree dependency
React:
Vue:
Vanilla:
Reusable framework-aware behavior
React:
Vue:
Vanilla:
Form two-way synchronization
React:
Vue:
Vanilla:
List identity
React:
Vue:
Vanilla:
Render elsewhere in DOM
React:
Vue:
Vanilla:
162. What Should Not Be Translated One-to-One
Some concepts look similar but should not be equated directly.
React useRef and Vue ref
Not the same conceptual default.
React useEffect and Vue watchEffect
Overlap, but different runtime models.
React useMemo and Vue computed
Both can cache derived work, but Vue computed participates directly in dependency-tracked reactivity.
React Context and a global store
Context distributes a value through a tree. It is not automatically a full store architecture.
Vue provide/inject and a global store
Same warning.
JSX and Vue templates
Both express UI structure, but compiler/runtime behavior differs.
Virtual DOM
React and Vue may both use virtual DOM concepts, but their reactivity and update strategies are not identical.
163. What Does Translate Almost Perfectly
These concepts are stable across frameworks:
These are the architectural ideas worth remembering.
164. Final Rosetta Stone Diagram
The architecture comes first.
The implementation vocabulary comes second.
165. Closing Perspective
A developer who knows only one framework can easily confuse:
with:
This appendix is designed to prevent that.
React, Vue, and Vanilla JavaScript differ significantly in their rendering and reactivity models.
But mature applications in all three still need answers to the same questions:
Those questions are the transferable knowledge.
If you move from React to Vue, do not search only for renamed Hooks.
If you move from Vue to React, do not try to recreate automatic dependency tracking.
If you move to Vanilla JavaScript, do not abandon component boundaries merely because the browser does not impose one component model.
Instead:
- identify the architectural responsibility;
- understand the target runtime;
- express the responsibility naturally in that runtime.
That is the purpose of a Rosetta Stone.
It translates meaning, not merely words.