Skip to content

JavaScript Ecosystem

The JavaScript ecosystem is vast and ever-evolving. This comprehensive guide covers popular frameworks, libraries, tools, and best practices for navigating the modern JavaScript landscape.

Terminal window
# Initialize a new project
npm init -y
# Install dependencies
npm install lodash express
npm install --save-dev jest eslint prettier
# Install globally
npm install -g nodemon typescript
# Install specific versions
npm install lodash@4.17.21
npm install react@^18.0.0 # Compatible version
npm install vue@~3.2.0 # Patch-level changes
# Update packages
npm update
npm update lodash
npm outdated
# Remove packages
npm uninstall lodash
npm prune # Remove unused packages
# Scripts in package.json
{
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest",
"build": "webpack --mode production",
"lint": "eslint src/**/*.js",
"format": "prettier --write src/**/*.js"
}
}
# Running scripts
npm run dev
npm test
npm run build
Terminal window
# Install Yarn
npm install -g yarn
# Initialize project
yarn init
# Add dependencies
yarn add lodash express
yarn add --dev jest eslint
# Install all dependencies
yarn install
# Update dependencies
yarn upgrade
yarn upgrade lodash
# Remove dependencies
yarn remove lodash
# Workspaces (monorepo)
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*"
]
}
yarn workspace package-a add lodash
yarn workspaces run test
Terminal window
# Install pnpm
npm install -g pnpm
# Basic commands (similar to npm)
pnpm install
pnpm add lodash
pnpm remove lodash
# Advantages of pnpm
# - Disk space efficient (content-addressable store)
# - Faster installation
# - Strict dependency resolution
// React Component Patterns
import React, { useState, useEffect, useContext, createContext } from 'react';
// Functional Component with Hooks
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('User not found');
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;
return (
<div className="user-profile">
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>Joined: {new Date(user.createdAt).toLocaleDateString()}</p>
</div>
);
}
// Context for State Management
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Custom Hooks
function useAPI(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (!cancelled) {
setData(result);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url]);
return { data, loading, error };
}
// Higher-Order Component
function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check authentication status
checkAuth().then(authenticated => {
setIsAuthenticated(authenticated);
setLoading(false);
});
}, []);
if (loading) return <div>Checking authentication...</div>;
if (!isAuthenticated) return <div>Please log in</div>;
return <WrappedComponent {...props} />;
};
}
// React Performance Optimization
import { memo, useMemo, useCallback } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ items, onItemClick }) {
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
const handleItemClick = useCallback((item) => {
onItemClick(item);
}, [onItemClick]);
return (
<div>
<p>Total Value: {expensiveValue}</p>
{items.map(item => (
<div key={item.id} onClick={() => handleItemClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// Vue 3 Composition API
import { ref, reactive, computed, onMounted, watch } from 'vue';
// Composition function
function useUser(userId) {
const user = ref(null);
const loading = ref(true);
const error = ref(null);
const fetchUser = async () => {
try {
loading.value = true;
error.value = null;
const response = await fetch(`/api/users/${userId.value}`);
if (!response.ok) throw new Error('User not found');
user.value = await response.json();
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
watch(userId, fetchUser, { immediate: true });
return { user, loading, error, refetch: fetchUser };
}
// Vue Component
export default {
name: 'UserProfile',
props: {
userId: {
type: Number,
required: true
}
},
setup(props) {
const userId = ref(props.userId);
const { user, loading, error } = useUser(userId);
const displayName = computed(() => {
return user.value ? `${user.value.firstName} ${user.value.lastName}` : '';
});
return {
user,
loading,
error,
displayName
};
},
template: `
<div class="user-profile">
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else-if="user">
<h2>{{ displayName }}</h2>
<p>{{ user.email }}</p>
</div>
</div>
`
};
// Pinia State Management
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', () => {
const users = ref([]);
const currentUser = ref(null);
const loading = ref(false);
const getUserById = computed(() => {
return (id) => users.value.find(user => user.id === id);
});
async function fetchUsers() {
loading.value = true;
try {
const response = await fetch('/api/users');
users.value = await response.json();
} catch (error) {
console.error('Failed to fetch users:', error);
} finally {
loading.value = false;
}
}
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
const newUser = await response.json();
users.value.push(newUser);
return newUser;
}
function setCurrentUser(user) {
currentUser.value = user;
}
return {
users,
currentUser,
loading,
getUserById,
fetchUsers,
createUser,
setCurrentUser
};
});
// Angular Service
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class UserService {
private users$ = new BehaviorSubject<User[]>([]);
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users').pipe(
map(users => {
this.users$.next(users);
return users;
}),
catchError(error => {
console.error('Failed to fetch users:', error);
throw error;
})
);
}
getUserById(id: number): Observable<User> {
return this.http.get<User>(`/api/users/${id}`);
}
createUser(user: Partial<User>): Observable<User> {
return this.http.post<User>('/api/users', user);
}
updateUser(id: number, user: Partial<User>): Observable<User> {
return this.http.put<User>(`/api/users/${id}`, user);
}
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`/api/users/${id}`);
}
}
// Angular Component
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-user-profile',
template: `
<div class="user-profile">
<div *ngIf="loading">Loading...</div>
<div *ngIf="error" class="error">{{ error }}</div>
<div *ngIf="user && !loading">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<button (click)="editUser()">Edit</button>
</div>
</div>
`,
styleUrls: ['./user-profile.component.css']
})
export class UserProfileComponent implements OnInit, OnDestroy {
user: User | null = null;
loading = true;
error: string | null = null;
private subscription = new Subscription();
constructor(
private userService: UserService,
private route: ActivatedRoute
) {}
ngOnInit() {
const userId = Number(this.route.snapshot.paramMap.get('id'));
this.subscription.add(
this.userService.getUserById(userId).subscribe({
next: user => {
this.user = user;
this.loading = false;
},
error: error => {
this.error = error.message;
this.loading = false;
}
})
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
editUser() {
// Navigate to edit component
}
}
import { createSlice, configureStore, createAsyncThunk } from '@reduxjs/toolkit';
// Async thunk for API calls
export const fetchUsers = createAsyncThunk(
'users/fetchUsers',
async (_, { rejectWithValue }) => {
try {
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch users');
return await response.json();
} catch (error) {
return rejectWithValue(error.message);
}
}
);
export const createUser = createAsyncThunk(
'users/createUser',
async (userData, { rejectWithValue }) => {
try {
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');
return await response.json();
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// Redux slice
const usersSlice = createSlice({
name: 'users',
initialState: {
entities: [],
loading: false,
error: null
},
reducers: {
clearError: (state) => {
state.error = null;
},
updateUser: (state, action) => {
const { id, updates } = action.payload;
const existingUser = state.entities.find(user => user.id === id);
if (existingUser) {
Object.assign(existingUser, updates);
}
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.loading = false;
state.entities = action.payload;
})
.addCase(fetchUsers.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
})
.addCase(createUser.fulfilled, (state, action) => {
state.entities.push(action.payload);
});
}
});
export const { clearError, updateUser } = usersSlice.actions;
// Store configuration
const store = configureStore({
reducer: {
users: usersSlice.reducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE']
}
})
});
export default store;
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// Simple store
const useCounterStore = create((set, get) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
double: () => set((state) => ({ count: state.count * 2 }))
}));
// Complex store with async actions
const useUserStore = create(
devtools(
persist(
(set, get) => ({
users: [],
currentUser: null,
loading: false,
error: null,
// Actions
setLoading: (loading) => set({ loading }),
setError: (error) => set({ error }),
setUsers: (users) => set({ users }),
setCurrentUser: (user) => set({ currentUser: user }),
// Async actions
fetchUsers: async () => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch users');
const users = await response.json();
set({ users, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
createUser: async (userData) => {
set({ loading: true, error: null });
try {
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');
const newUser = await response.json();
set((state) => ({
users: [...state.users, newUser],
loading: false
}));
return newUser;
} catch (error) {
set({ error: error.message, loading: false });
throw error;
}
},
updateUser: async (id, updates) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
});
if (!response.ok) throw new Error('Failed to update user');
const updatedUser = await response.json();
set((state) => ({
users: state.users.map(user =>
user.id === id ? updatedUser : user
),
loading: false
}));
return updatedUser;
} catch (error) {
set({ error: error.message, loading: false });
throw error;
}
},
deleteUser: async (id) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/users/${id}`, {
method: 'DELETE'
});
if (!response.ok) throw new Error('Failed to delete user');
set((state) => ({
users: state.users.filter(user => user.id !== id),
loading: false
}));
} catch (error) {
set({ error: error.message, loading: false });
throw error;
}
},
// Computed values
getUserById: (id) => {
const { users } = get();
return users.find(user => user.id === id);
},
getActiveUsers: () => {
const { users } = get();
return users.filter(user => user.active);
}
}),
{
name: 'user-store',
partialize: (state) => ({
currentUser: state.currentUser,
users: state.users
})
}
),
{ name: 'user-store' }
)
);
// Usage in React component
function UserList() {
const {
users,
loading,
error,
fetchUsers,
createUser,
getActiveUsers
} = useUserStore();
const activeUsers = useUserStore(getActiveUsers);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h2>Users ({activeUsers.length} active)</h2>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
entry: {
main: './src/index.js',
vendor: ['react', 'react-dom', 'lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProduction
? '[name].[contenthash].js'
: '[name].js',
chunkFilename: isProduction
? '[name].[contenthash].chunk.js'
: '[name].chunk.js',
publicPath: '/',
clean: true
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
'@babel/preset-react'
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-class-properties'
]
}
}
},
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.scss$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: 'asset/resource',
generator: {
filename: 'images/[name].[hash][ext]'
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
minify: isProduction
}),
...(isProduction ? [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[name].[contenthash].chunk.css'
})
] : [])
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
},
...(isProduction && {
minimizer: [
'...',
new CssMinimizerPlugin()
]
})
},
devServer: {
contentBase: './dist',
port: 3000,
hot: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true
}
}
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils')
}
}
};
};
vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@components': resolve(__dirname, 'src/components'),
'@utils': resolve(__dirname, 'src/utils')
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
ui: ['@mui/material', '@emotion/react']
}
}
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
optimizeDeps: {
include: ['lodash', 'axios']
}
});

This comprehensive ecosystem guide provides a foundation for understanding the modern JavaScript landscape. The ecosystem continues to evolve rapidly, so staying updated with the latest tools and best practices is essential.


Explore specific frameworks and tools based on your project requirements and team expertise.