diff --git a/docs/knowledge/web-development/index.md b/docs/knowledge/web-development/index.md
index 22c8282..bf7d849 100644
--- a/docs/knowledge/web-development/index.md
+++ b/docs/knowledge/web-development/index.md
@@ -19,6 +19,8 @@ Frameworks, patterns, and practical notes for building and shipping web applicat
actually run, and how to deal with background tasks.
- [TypeScript Fundamentals](typescript-fundamentals.md) — typing properly:
interfaces, generics, narrowing, and utility types.
+- [JavaScript Fundamentals](javascript-fundamentals.md) — the Call Stack, Event Loop, tasks and microtasks, Promises, async/await, and closures.
+- [React Principles](react-principles.md) — rendering, state, Effects, refs, Context, server state, React Query, memoization, SSR/SSG/CSR, WebSockets, accessibility, Tailwind, and performance.
- [REST vs. GraphQL](rest-vs-graphql.md) — resource/verb design vs. a
single-endpoint query language, over-/under-fetching, and GraphQL's own
N+1 problem.
diff --git a/docs/knowledge/web-development/javascript-fundamentals.md b/docs/knowledge/web-development/javascript-fundamentals.md
new file mode 100644
index 0000000..ea69b5d
--- /dev/null
+++ b/docs/knowledge/web-development/javascript-fundamentals.md
@@ -0,0 +1,199 @@
+---
+tags:
+
+- web-development
+- javascript
+- async
+- event-loop
+- closures
+
+---
+
+# JavaScript Fundamentals: Event Loop, Promises & Closures
+
+JavaScript is single-threaded at the level of a JavaScript execution context: one Call Stack executes JavaScript at a time. This is not the same thing as Python's CPython GIL. The runtime can provide multiple execution contexts, such as Web Workers, while the main JavaScript context still has one Call Stack.
+
+The browser or Node.js runtime provides timers, networking, DOM events, and the Event Loop around the JavaScript engine.
+
+## The Call Stack
+
+Synchronous JavaScript is executed on the Call Stack:
+
+\`\`\`javascript
+console.log("A");
+console.log("B");
+console.log("C");
+\`\`\`
+
+A long-running synchronous operation blocks the stack. The runtime does not automatically move arbitrary CPU-heavy JavaScript elsewhere.
+
+## The Event Loop
+
+The Event Loop coordinates when asynchronous work can return to the JavaScript Call Stack.
+
+\`\`\`
+JavaScript
+ |
+ v
+Call Stack
+ |
+ +----> Runtime APIs
+ | +--> timers
+ | +--> fetch/networking
+ | +--> DOM events
+ |
+ v
+Queues
+ |
+ v
+Event Loop
+ |
+ v
+Call Stack
+\`\`\`
+
+Async and await are not required for the Event Loop to work.
+
+\`\`\`javascript
+console.log("A");
+
+setTimeout(() => {
+ console.log("B");
+}, 0);
+
+console.log("C");
+\`\`\`
+
+The result is:
+
+\`\`\`
+A
+C
+B
+\`\`\`
+
+A zero-delay timer does not mean "execute immediately". The callback still waits until it is eligible and the JavaScript stack is available.
+
+## Tasks and Microtasks
+
+In simplified terms:
+
+- Tasks (often called macrotasks) include timer callbacks and many event callbacks.
+- Microtasks include Promise continuations such as then, catch, finally, and queueMicrotask.
+- After the current synchronous work finishes, pending microtasks are processed before the runtime proceeds to the next task.
+
+\`\`\`javascript
+console.log("A");
+
+setTimeout(() => {
+ console.log("B");
+}, 0);
+
+Promise.resolve().then(() => {
+ console.log("C");
+});
+
+console.log("D");
+\`\`\`
+
+The result is:
+
+\`\`\`
+A
+D
+C
+B
+\`\`\`
+
+## Promises and async/await
+
+A Promise represents the eventual result of an asynchronous operation.
+
+\`\`\`javascript
+fetch("/api/users")
+ .then(response => response.json())
+ .then(users => {
+ console.log(users);
+ });
+\`\`\`
+
+Async/await is syntax for working with Promise-based asynchronous code:
+
+\`\`\`javascript
+async function loadUsers() {
+ const response = await fetch("/api/users");
+ const users = await response.json();
+
+ return users;
+}
+\`\`\`
+
+Await does not block the entire JavaScript runtime. It suspends the current async function's continuation while the Promise is pending.
+
+## Closures
+
+A closure happens when a function retains access to variables from the lexical scope where it was created, even after the outer function has returned.
+
+\`\`\`javascript
+function createCounter() {
+ let count = 0;
+
+ return function () {
+ count += 1;
+ return count;
+ };
+}
+
+const counter = createCounter();
+
+console.log(counter()); // 1
+console.log(counter()); // 2
+console.log(counter()); // 3
+\`\`\`
+
+Closures are useful for private state, function factories, callbacks, event handlers, and maintaining state across calls.
+
+They are also fundamental to React. A function created during a render closes over the values from that render:
+
+\`\`\`jsx
+function Counter() {
+ const [count, setCount] = useState(0);
+
+ function handleClick() {
+ console.log(count);
+ }
+
+ // handleClick closes over this render's count.
+}
+\`\`\`
+
+## Event Loop vs. Python asyncio
+
+The mental model is similar to FastAPI's asyncio model:
+
+| Python / FastAPI | JavaScript |
+|---|---|
+| asyncio event loop | runtime Event Loop |
+| coroutine | async function |
+| await | await |
+| async I/O | runtime APIs such as fetch |
+| task scheduling | callback/Promise scheduling |
+
+The analogy should not be taken literally. CPython's GIL is a locking mechanism related to Python bytecode execution; the JavaScript Event Loop is a scheduling mechanism.
+
+## Practical Rule
+
+When analyzing asynchronous JavaScript, ask:
+
+1. What is executing synchronously on the Call Stack?
+1. What operation is being handled by the runtime?
+1. Does it produce a Promise or callback?
+1. Which queue will the continuation enter?
+1. When will the Event Loop allow it to execute?
+
+## Related Articles
+
+- [TypeScript Fundamentals](typescript-fundamentals.md)
+- [FastAPI Event Loop](fastapi-event-loop.md)
+- [WebSockets](websockets.md)
+- [React Principles](react-principles.md)
diff --git a/docs/knowledge/web-development/react-principles.md b/docs/knowledge/web-development/react-principles.md
new file mode 100644
index 0000000..4fc4f70
--- /dev/null
+++ b/docs/knowledge/web-development/react-principles.md
@@ -0,0 +1,470 @@
+---
+tags:
+
+- web-development
+- react
+- javascript
+- typescript
+- frontend
+
+---
+
+# React Principles
+
+React becomes easier to reason about when the mental model is clear: state changes cause renders, props flow through component boundaries, Effects synchronize with external systems, and different kinds of state should be managed differently.
+
+## Rendering and State
+
+A simplified React cycle is:
+
+\`\`\`
+User interaction
+ |
+ v
+setState / dispatch
+ |
+ v
+React renders
+ |
+ v
+React commits UI changes
+ |
+ v
+Effects run when their dependencies require them
+\`\`\`
+
+State should be treated as immutable data:
+
+\`\`\`javascript
+// Bad
+user.name = name;
+setUser(user);
+
+// Good
+setUser({
+ ...user,
+ name,
+});
+\`\`\`
+
+When updating state based on previous state, use the functional form:
+
+\`\`\`javascript
+setCount(current => current + 1);
+setCount(current => current + 1);
+setCount(current => current + 1);
+\`\`\`
+
+Using setCount(count + 1) three times can reuse the same value from the current render.
+
+## Derived Data
+
+Do not create state for values that can be calculated from existing state.
+
+Prefer:
+
+\`\`\`javascript
+const filteredProducts = products.filter(
+ product => product.name.includes(search)
+);
+\`\`\`
+
+over maintaining filteredProducts as separate state and synchronizing it with an Effect.
+
+This avoids duplicated state and unnecessary Effects.
+
+## useEffect
+
+useEffect is primarily for synchronizing React with an external system such as APIs, browser APIs, timers, WebSockets, subscriptions, and third-party libraries.
+
+\`\`\`javascript
+useEffect(fn); // after every render
+useEffect(fn, []); // after the initial mount
+useEffect(fn, [x]); // after mount and when x changes
+\`\`\`
+
+The dependency array describes reactive values the Effect depends on.
+
+For an API request that should happen after the component mounts:
+
+\`\`\`javascript
+useEffect(() => {
+ getProducts();
+}, []);
+\`\`\`
+
+External resources should be cleaned up:
+
+\`\`\`javascript
+useEffect(() => {
+ const socket = new WebSocket("wss://example.com/events");
+
+ socket.onmessage = event => {
+ // update state
+ };
+
+ return () => {
+ socket.close();
+ };
+}, []);
+\`\`\`
+
+## Race Conditions in Effects
+
+Search requests can race:
+
+\`\`\`
+"j" -> request A
+"jo" -> request B
+"joh" -> request C
+"john" -> request D
+\`\`\`
+
+Responses are not guaranteed to finish in that order. An older request can overwrite newer data.
+
+Use cancellation when the previous request is obsolete:
+
+\`\`\`javascript
+useEffect(() => {
+ const controller = new AbortController();
+
+ fetch(\`/api/users?q=\${query}\`, {
+ signal: controller.signal,
+ })
+ .then(response => response.json())
+ .then(setUsers)
+ .catch(error => {
+ if (error.name !== "AbortError") {
+ console.error(error);
+ }
+ });
+
+ return () => controller.abort();
+}, [query]);
+\`\`\`
+
+For search inputs, debouncing can also prevent unnecessary requests.
+
+## useRef
+
+useRef is a persistent mutable container whose changes do not trigger a render.
+
+The simplest use is a DOM reference:
+
+\`\`\`jsx
+const inputRef = useRef(null);
+
+return (
+ <>
+
+
+ >
+);
+\`\`\`
+
+A useful rule:
+
+- useState: remember a value and update the UI when it changes.
+- useRef: remember a value without causing a render when it changes.
+
+Refs are also useful for timers, previous values, AbortControllers, and other imperative values.
+
+## useReducer
+
+When several state transitions belong together, useReducer can make the state model explicit:
+
+\`\`\`javascript
+const [state, dispatch] = useReducer(reducer, initialState);
+
+dispatch({ type: "SAVE_START" });
+dispatch({ type: "SAVE_SUCCESS", payload: product });
+\`\`\`
+
+The model becomes:
+
+\`\`\`
+UI event
+ |
+ v
+dispatch(action)
+ |
+ v
+reducer
+ |
+ v
+new state
+ |
+ v
+render
+\`\`\`
+
+This is also useful preparation for Redux.
+
+## Context and Prop Drilling
+
+Prop drilling happens when data is passed through components that do not use it only so a deeper component can receive it.
+
+Context can provide values to descendants without explicitly passing them through every intermediate component.
+
+Context is not a universal state-management solution. It is primarily a mechanism for making values available to a component subtree.
+
+## Client State vs. Server State
+
+Client state includes modal visibility, selected product, sidebar state, theme, local form state, and cart state.
+
+Server state includes products, orders, customers, and dashboard statistics from an API.
+
+Server state has additional concerns:
+
+- caching
+- stale data
+- refetching
+- retries
+- deduplication
+- synchronization
+
+## React Query
+
+A custom hook can manually implement fetching, loading, error, data, caching, retries, refetching, and deduplication.
+
+TanStack Query provides an abstraction around server state:
+
+\`\`\`javascript
+const {
+ data,
+ isLoading,
+ error,
+} = useQuery({
+ queryKey: ["products", search],
+ queryFn: () => getProducts(search),
+});
+\`\`\`
+
+The important concept is understanding why server state is different from local UI state.
+
+## RTK Query
+
+RTK Query solves a similar server-state/data-fetching problem within the Redux Toolkit ecosystem.
+
+\`\`\`
+React Query / TanStack Query
+ |
+ +-- server-state/data-fetching abstraction
+
+RTK Query
+ |
+ +-- server-state/data-fetching integrated with Redux Toolkit
+\`\`\`
+
+Choose based on the application's state architecture rather than assuming all state belongs in Redux.
+
+## useMemo, useCallback and React.memo
+
+useMemo memoizes a calculated value:
+
+\`\`\`javascript
+const filteredProducts = useMemo(() => {
+ return products
+ .filter(...)
+ .sort(...);
+}, [products, search, sort]);
+\`\`\`
+
+useCallback memoizes a function reference:
+
+\`\`\`javascript
+const handleSelect = useCallback((product) => {
+ setSelectedProduct(product);
+}, []);
+\`\`\`
+
+React.memo allows a component to skip rendering when its props are equal according to its comparison.
+
+\`\`\`
+useMemo -> memoized value
+useCallback -> memoized function
+React.memo -> memoized component rendering
+\`\`\`
+
+Do not use memoization everywhere. It is a performance optimization, not a correctness requirement. A good reason for useCallback is maintaining a stable callback passed to a memoized child. A good reason for useMemo is avoiding an expensive calculation when its inputs have not changed.
+
+## Keys
+
+Keys allow React to identify list items across renders:
+
+\`\`\`jsx
+users.map(user => (
+
+))
+\`\`\`
+
+Stable IDs are preferable to array indexes when items can be inserted, removed, or reordered.
+
+## Component Architecture
+
+A large component containing API calls, form state, validation, tables, pagination, modals, business rules, and formatting has too many responsibilities.
+
+A useful decomposition is:
+
+\`\`\`
+Page
+ |
+ +-- ProductForm
+ +-- ProductTable
+ +-- Pagination
+ +-- ProductModal
+
+Data / behavior
+ |
+ +-- useProducts()
+ +-- usePagination()
+ +-- useProductForm()
+
+Pure logic
+ |
+ +-- formatPrice()
+ +-- validateProduct()
+\`\`\`
+
+The goal is not to split code simply because it can be split. The goal is to separate responsibilities and make boundaries meaningful.
+
+## SSR, SSG and CSR
+
+CSR means Client-Side Rendering: the browser loads the JavaScript application and React renders the page on the client.
+
+SSR means Server-Side Rendering: the server generates HTML for a request and sends it to the browser.
+
+SSG means Static Site Generation: HTML is generated ahead of time, usually during a build, and served as static content.
+
+A simple mental model:
+
+\`\`\`
+CSR -> browser generates
+SSR -> server generates per request
+SSG -> generated ahead of time
+\`\`\`
+
+Hydration is the process of React attaching its behavior to server-rendered HTML so the page becomes interactive.
+
+SSR is a rendering strategy; it is not automatically a security mechanism.
+
+## WebSockets
+
+WebSockets are useful when communication needs to be persistent and bidirectional:
+
+\`\`\`
+HTTP
+Client ---- request ----> Server
+Client <--- response ---- Server
+
+WebSocket
+Client <=================> Server
+ persistent
+ full-duplex
+\`\`\`
+
+In React, a WebSocket is an external system, so its lifecycle fits naturally into an Effect:
+
+\`\`\`javascript
+useEffect(() => {
+ const socket = new WebSocket("wss://example.com/events");
+
+ socket.onmessage = event => {
+ const data = JSON.parse(event.data);
+ setNotifications(current => [...current, data]);
+ };
+
+ return () => socket.close();
+}, []);
+\`\`\`
+
+For server-only push, SSE can be simpler. For infrequent updates, polling may be sufficient.
+
+See [WebSockets](websockets.md) for the protocol and scaling details.
+
+## Accessibility
+
+Accessibility should be part of component design, not a final check.
+
+Prefer semantic HTML:
+
+Use a semantic button element, for example: `button` with the accessible name `Save`.
+
+over a clickable generic element.
+
+Important areas include semantic HTML, labels, keyboard navigation, focus management, color contrast, accessible names, alternative text, appropriate ARIA usage, and screen-reader behavior.
+
+WCAG organizes accessibility around four principles:
+
+- Perceivable
+- Operable
+- Understandable
+- Robust
+
+Google Lighthouse and tools such as axe can audit an application.
+
+Reference: [W3C Web Accessibility Initiative](https://www.w3.org/WAI/standards-guidelines/wcag/)
+
+## Tailwind CSS
+
+Tailwind provides utility classes that can be composed directly in markup:
+
+For example, a button can compose utilities such as `px-4`, `py-2`, `rounded-md`, and `font-medium`.
+
+Know responsive utilities, spacing, flexbox/grid, hover/focus states, breakpoints, dark mode, and utility composition.
+
+Tailwind's JIT approach generates CSS from the classes used by the project instead of requiring a large pre-generated utility stylesheet.
+
+The qualification goal is practical fluency rather than memorizing every class.
+
+## Performance
+
+Performance optimization should start with measurement.
+
+Useful tools include React DevTools Profiler, Chrome DevTools Performance and Memory, and Lighthouse.
+
+Ask:
+
+1. Which component rendered?
+1. Why did it render?
+1. How long did it take?
+1. Is the work actually expensive?
+1. Can the problem be solved structurally before adding memoization?
+
+For very large lists, virtualization renders only the visible portion instead of creating DOM nodes for every item.
+
+## Qualification Mental Model
+
+\`\`\`
+State
+ |
+ +-- local UI state -> useState
+ +-- complex transitions -> useReducer
+ +-- shared client state -> Context / Redux
+ +-- server state -> React Query / RTK Query
+
+External systems
+ |
+ +-- API -> Effect / data-fetching library
+ +-- WebSocket -> Effect + cleanup
+ +-- DOM -> useRef
+
+Performance
+ |
+ +-- measure first
+ +-- React.memo
+ +-- useMemo
+ +-- useCallback
+ +-- virtualization
+\`\`\`
+
+The strongest answers explain the reason behind each tool, what problem it solves, and what complexity it introduces.
+
+## Related Articles
+
+- [JavaScript Fundamentals: Event Loop, Promises & Closures](javascript-fundamentals.md)
+- [TypeScript Fundamentals](typescript-fundamentals.md)
+- [WebSockets](websockets.md)
+- [FastAPI Event Loop](fastapi-event-loop.md)
diff --git a/docs/knowledge/web-development/typescript-fundamentals.md b/docs/knowledge/web-development/typescript-fundamentals.md
index a3e8c12..8c7a5d4 100644
--- a/docs/knowledge/web-development/typescript-fundamentals.md
+++ b/docs/knowledge/web-development/typescript-fundamentals.md
@@ -195,6 +195,41 @@ type UserMap = Record; // { [key: number]: User }
type ReadonlyUser = Readonly; // all properties readonly
```
+## TypeScript in React
+
+React components are functions, so the same typing principles apply to props, state, events, and reusable components.
+
+A simple component can type its props directly:
+
+```tsx
+type Product = {
+ id: number;
+ name: string;
+ price: number;
+};
+
+function ProductCard({ product }: { product: Product }) {
+ return
{product.name}
;
+}
+```
+
+Generics become useful for reusable components. For example, a generic list can preserve the relationship between its item type and its render function:
+
+```tsx
+type ListProps = {
+ items: T[];
+ renderItem: (item: T) => React.ReactNode;
+};
+
+function List({ items, renderItem }: ListProps) {
+ return <>{items.map(renderItem)}>;
+}
+```
+
+This allows the same component to work with different data while keeping the item type available inside renderItem.
+
+For API boundaries, prefer explicit domain types and avoid allowing any to spread through the application. If the external data is not trusted, unknown forces the application to validate or narrow it before use.
+
## Best Practices
- Enable `strict` mode in `tsconfig.json` (`"strict": true`) — it turns on