From samhvw8-dot-claude
Multi-framework frontend development. Frameworks: React 18+ (Suspense, hooks, TanStack), Vue 3 (Composition API, Pinia, Nuxt), Svelte 5 (Runes, SvelteKit), Angular (Signals, standalone). Common: TypeScript, state management, routing, data fetching, performance optimization, component patterns. Actions: create, build, implement, style, optimize, refactor components/pages/features. Keywords: React, Vue, Svelte, Angular, component, TypeScript, hooks, Composition API, runes, signals, useSuspenseQuery, Pinia, stores, state management, routing, lazy loading, Suspense, performance, bundle size, code splitting, reactivity, props, events. Use when: creating components in any framework, building pages, fetching data, implementing routing, state management, optimizing performance, organizing frontend code, choosing between frameworks.
npx claudepluginhub joshuarweaver/cascade-code-languages-misc-1 --plugin samhvw8-dot-claudeThis skill uses the workspace's default tool permissions.
Comprehensive guide for modern frontend development across React, Vue 3, Svelte 5, and Angular. Covers framework-specific patterns, common architectural principles, and cross-framework best practices.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Comprehensive guide for modern frontend development across React, Vue 3, Svelte 5, and Angular. Covers framework-specific patterns, common architectural principles, and cross-framework best practices.
Choose your framework based on project requirements:
| Framework | Best For | Learning Curve | Performance | Ecosystem |
|---|---|---|---|---|
| React | Large apps, strong typing, enterprise | Medium | Good | Largest |
| Vue 3 | Progressive adoption, approachable | Low | Excellent | Growing |
| Svelte 5 | Small/medium apps, native feel | Low | Best | Smaller |
| Angular | Enterprise, full-featured | Steep | Good | Complete |
Quick Decision:
See framework-specific sections below for detailed patterns.
| Feature | React | Vue 3 | Svelte 5 | Angular |
|---|---|---|---|---|
| Reactivity | Hooks (useState) | ref/reactive | Runes ($state) | Signals |
| Components | JSX/TSX | SFC (.vue) | SFC (.svelte) | Decorators/Class |
| State Mgmt | Zustand/Context | Pinia | Stores | Services |
| Routing | React Router/TanStack | Vue Router | SvelteKit | Angular Router |
| Data Fetching | TanStack Query | Composables/VueQuery | Load functions | HttpClient/RxJS |
| Styling | CSS-in-JS/Modules | Scoped CSS | Scoped CSS | Component styles |
| Full-Stack | Next.js | Nuxt | SvelteKit | Universal/SSR |
| Bundle Size | ~40KB | ~32KB | ~3KB | ~60KB |
| Compiler | Runtime | Runtime | Compile-time | AOT Compiler |
Jump to:
Creating a component? Follow this checklist:
React.FC<Props> pattern with TypeScriptReact.lazy(() => import())<SuspenseLoader> for loading statesuseSuspenseQuery for data fetching@/, ~types, ~components, ~featuresuseCallback for event handlers passed to childrenuseMuiSnackbar for user notificationsCreating a feature? Set up this structure:
features/{feature-name}/ directoryapi/, components/, hooks/, helpers/, types/api/{feature}Api.tstypes/routes/{feature-name}/index.tsxindex.ts| Alias | Resolves To | Example |
|---|---|---|
@/ | src/ | import { apiClient } from '@/lib/apiClient' |
~types | src/types | import type { User } from '~types/user' |
~components | src/components | import { SuspenseLoader } from '~components/SuspenseLoader' |
~features | src/features | import { authApi } from '~features/auth' |
Defined in: vite.config.ts lines 180-185
// React & Lazy Loading
import React, { useState, useCallback, useMemo } from 'react';
const Heavy = React.lazy(() => import('./Heavy'));
// MUI Components
import { Box, Paper, Typography, Button, Grid } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';
// TanStack Query (Suspense)
import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query';
// TanStack Router
import { createFileRoute } from '@tanstack/react-router';
// Project Components
import { SuspenseLoader } from '~components/SuspenseLoader';
// Hooks
import { useAuth } from '@/hooks/useAuth';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
// Types
import type { Post } from '~types/post';
Modern React components use:
React.FC<Props> for type safetyReact.lazy() for code splittingSuspenseLoader for loading statesKey Concepts:
📖 Complete Guide: resources/component-patterns.md
PRIMARY PATTERN: useSuspenseQuery
isLoading checksAPI Service Layer:
features/{feature}/api/{feature}Api.tsapiClient axios instance/form/route (NOT /api/form/route)📖 Complete Guide: resources/data-fetching.md
features/ vs components/:
features/: Domain-specific (posts, comments, auth)components/: Truly reusable (SuspenseLoader, CustomAppBar)Feature Subdirectories:
features/
my-feature/
api/ # API service layer
components/ # Feature components
hooks/ # Custom hooks
helpers/ # Utility functions
types/ # TypeScript types
📖 Complete Guide: resources/file-organization.md
Inline vs Separate:
const styles: Record<string, SxProps<Theme>>100 lines: Separate
.styles.tsfile
Primary Method:
sx prop for MUI componentsSxProps<Theme>(theme) => theme.palette.primary.mainMUI v7 Grid:
<Grid size={{ xs: 12, md: 6 }}> // ✅ v7 syntax
<Grid xs={12} md={6}> // ❌ Old syntax
📖 Complete Guide: resources/styling-guide.md
TanStack Router - Folder-Based:
routes/my-route/index.tsxcreateFileRouteExample:
import { createFileRoute } from '@tanstack/react-router';
import { lazy } from 'react';
const MyPage = lazy(() => import('@/features/my-feature/components/MyPage'));
export const Route = createFileRoute('/my-route/')({
component: MyPage,
loader: () => ({ crumb: 'My Route' }),
});
📖 Complete Guide: resources/routing-guide.md
CRITICAL RULE: No Early Returns
// ❌ NEVER - Causes layout shift
if (isLoading) {
return <LoadingSpinner />;
}
// ✅ ALWAYS - Consistent layout
<SuspenseLoader>
<Content />
</SuspenseLoader>
Why: Prevents Cumulative Layout Shift (CLS), better UX
Error Handling:
useMuiSnackbar for user feedbackreact-toastifyonError callbacks📖 Complete Guide: resources/loading-and-error-states.md
Optimization Patterns:
useMemo: Expensive computations (filter, sort, map)useCallback: Event handlers passed to childrenReact.memo: Expensive components📖 Complete Guide: resources/performance.md
Standards:
any typeimport type { User } from '~types/user'📖 Complete Guide: resources/typescript-standards.md
Covered Topics:
useAuth hook for current user📖 Complete Guide: resources/common-patterns.md
Full working examples:
📖 Complete Guide: resources/complete-examples.md
| Need to... | Read this resource |
|---|---|
| Create a React component | component-patterns.md |
| Fetch data with TanStack Query | data-fetching.md |
| Organize files/folders | file-organization.md |
| Style with MUI v7 | styling-guide.md |
| Set up TanStack Router | routing-guide.md |
| Handle loading/errors | loading-and-error-states.md |
| Optimize React performance | performance.md |
| TypeScript types | typescript-standards.md |
| Forms/Auth/DataGrid | common-patterns.md |
| See full React examples | complete-examples.md |
Note: Resources above are React-specific. For Vue/Svelte/Angular, see framework sections in this document.
src/
features/
my-feature/
api/
myFeatureApi.ts # API service
components/
MyFeature.tsx # Main component
SubComponent.tsx # Related components
hooks/
useMyFeature.ts # Custom hooks
useSuspenseMyFeature.ts # Suspense hooks
helpers/
myFeatureHelpers.ts # Utilities
types/
index.ts # TypeScript types
index.ts # Public exports
components/
SuspenseLoader/
SuspenseLoader.tsx # Reusable loader
CustomAppBar/
CustomAppBar.tsx # Reusable app bar
routes/
my-route/
index.tsx # Route component
create/
index.tsx # Nested route
import React, { useState, useCallback } from 'react';
import { Box, Paper } from '@mui/material';
import { useSuspenseQuery } from '@tanstack/react-query';
import { featureApi } from '../api/featureApi';
import type { FeatureData } from '~types/feature';
interface MyComponentProps {
id: number;
onAction?: () => void;
}
export const MyComponent: React.FC<MyComponentProps> = ({ id, onAction }) => {
const [state, setState] = useState<string>('');
const { data } = useSuspenseQuery({
queryKey: ['feature', id],
queryFn: () => featureApi.getFeature(id),
});
const handleAction = useCallback(() => {
setState('updated');
onAction?.();
}, [onAction]);
return (
<Box sx={{ p: 2 }}>
<Paper sx={{ p: 3 }}>
{/* Content */}
</Paper>
</Box>
);
};
export default MyComponent;
For complete examples, see resources/complete-examples.md
Composition API - Modern Vue 3 approach with <script setup>:
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { User } from '@/types/user'
interface Props {
userId: number
}
const props = defineProps<Props>()
const emit = defineEmits<{
update: [user: User]
}>()
const user = ref<User | null>(null)
const isLoading = ref(true)
const displayName = computed(() => user.value?.name ?? 'Unknown')
onMounted(async () => {
user.value = await fetchUser(props.userId)
isLoading.value = false
})
function handleUpdate() {
if (user.value) emit('update', user.value)
}
</script>
<template>
<div class="user-profile">
<div v-if="isLoading">Loading...</div>
<div v-else>
<h2>{{ displayName }}</h2>
<button @click="handleUpdate">Update</button>
</div>
</div>
</template>
<style scoped>
.user-profile {
padding: 1rem;
}
</style>
Key Patterns:
<script setup> - Concise composition API syntaxdefineProps<T>() - Type-safe props with genericsdefineEmits<T>() - Type-safe eventsref(), reactive() - Reactivity primitivescomputed() - Derived statewatch(), watchEffect() - Side effectsonMounted, onUnmounted - Lifecycle hooks// stores/userStore.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// State
const users = ref<User[]>([])
const currentUser = ref<User | null>(null)
// Getters
const userCount = computed(() => users.value.length)
const isAuthenticated = computed(() => currentUser.value !== null)
// Actions
async function fetchUsers() {
users.value = await api.getUsers()
}
function setCurrentUser(user: User) {
currentUser.value = user
}
return { users, currentUser, userCount, isAuthenticated, fetchUsers, setCurrentUser }
})
// Component usage
<script setup lang="ts">
import { useUserStore } from '@/stores/userStore'
const userStore = useUserStore()
</script>
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('@/views/HomeView.vue')
},
{
path: '/users/:id',
component: () => import('@/views/UserView.vue'),
props: true
}
]
})
// Component with route params
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const userId = route.params.id
function goBack() {
router.push('/')
}
</script>
Composables - Reusable logic:
// composables/useUser.ts
import { ref, type Ref } from 'vue'
export function useUser(id: Ref<number>) {
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
async function fetchUser() {
loading.value = true
try {
user.value = await api.getUser(id.value)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
watchEffect(() => {
fetchUser()
})
return { user, loading, error, refetch: fetchUser }
}
// Usage in component
<script setup lang="ts">
const props = defineProps<{ userId: number }>()
const { user, loading, error } = useUser(toRef(props, 'userId'))
</script>
Nuxt.js Server-Side:
<script setup lang="ts">
// Nuxt auto-imports composables
const { data: user, pending, error } = await useFetch(`/api/users/${route.params.id}`)
</script>
shallowRef() for large objectsSvelte 5 Runes - New reactivity system:
<script lang="ts">
import type { User } from '$lib/types/user'
interface Props {
userId: number
onUpdate?: (user: User) => void
}
let { userId, onUpdate }: Props = $props()
// Reactive state with $state
let user = $state<User | null>(null)
let isLoading = $state(true)
// Derived state with $derived
let displayName = $derived(user?.name ?? 'Unknown')
let userAge = $derived.by(() => {
if (!user?.birthDate) return null
return calculateAge(user.birthDate)
})
// Effects with $effect
$effect(() => {
// Runs when userId changes
loadUser(userId)
})
async function loadUser(id: number) {
isLoading = true
user = await fetchUser(id)
isLoading = false
}
function handleUpdate() {
if (user) onUpdate?.(user)
}
</script>
{#if isLoading}
<div>Loading...</div>
{:else if user}
<div class="user-profile">
<h2>{displayName}</h2>
{#if userAge}
<p>Age: {userAge}</p>
{/if}
<button onclick={handleUpdate}>Update</button>
</div>
{/if}
<style>
.user-profile {
padding: 1rem;
}
</style>
Svelte 5 Runes:
$state() - Reactive state (replaces let for reactivity)$derived - Computed values (replaces $:)$derived.by() - Complex derived state$effect() - Side effects (replaces $: statements)$props() - Component props with destructuring$bindable() - Two-way binding for props$inspect() - Debugging reactive values// stores/user.ts
import { writable, derived, readonly } from 'svelte/store'
function createUserStore() {
const { subscribe, set, update } = writable<User[]>([])
return {
subscribe,
setUsers: (users: User[]) => set(users),
addUser: (user: User) => update(users => [...users, user]),
removeUser: (id: number) => update(users => users.filter(u => u.id !== id)),
reset: () => set([])
}
}
export const users = createUserStore()
export const userCount = derived(users, $users => $users.length)
// Component usage (Svelte 4 style)
<script>
import { users } from '$lib/stores/user'
</script>
<p>Total users: {$users.length}</p>
// Or with runes (Svelte 5)
<script>
import { users } from '$lib/stores/user'
let currentUsers = $state($users)
</script>
// src/routes/users/[id]/+page.ts
import type { PageLoad } from './$types'
export const load: PageLoad = async ({ params, fetch }) => {
const user = await fetch(`/api/users/${params.id}`).then(r => r.json())
return {
user
}
}
// src/routes/users/[id]/+page.svelte
<script lang="ts">
import type { PageData } from './$types'
let { data }: { data: PageData } = $props()
let { user } = $derived(data)
</script>
<h1>{user.name}</h1>
SvelteKit Patterns:
+page.svelte - Page component+page.ts - Page data loading (runs on server and client)+page.server.ts - Server-only load functions+layout.svelte - Shared layouts+server.ts - API endpoints$state() instead of implicit let// user-profile.component.ts
import { Component, Input, Output, EventEmitter, signal, computed } from '@angular/core'
import { CommonModule } from '@angular/common'
import type { User } from '@/types/user'
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule],
template: `
<div class="user-profile" *ngIf="!isLoading(); else loading">
<h2>{{ displayName() }}</h2>
<p>Age: {{ userAge() }}</p>
<button (click)="handleUpdate()">Update</button>
</div>
<ng-template #loading>
<div>Loading...</div>
</ng-template>
`,
styles: [`
.user-profile {
padding: 1rem;
}
`]
})
export class UserProfileComponent {
@Input({ required: true }) userId!: number
@Output() userUpdate = new EventEmitter<User>()
// Signals - reactive primitives
user = signal<User | null>(null)
isLoading = signal(true)
// Computed signals
displayName = computed(() => this.user()?.name ?? 'Unknown')
userAge = computed(() => {
const birthDate = this.user()?.birthDate
return birthDate ? this.calculateAge(birthDate) : null
})
async ngOnInit() {
this.user.set(await this.fetchUser(this.userId))
this.isLoading.set(false)
}
handleUpdate() {
const currentUser = this.user()
if (currentUser) this.userUpdate.emit(currentUser)
}
}
Angular Signals - New reactivity system (v16+):
signal() - Writable reactive valuecomputed() - Derived stateeffect() - Side effects.set(), .update() - Modify signal values() - Read signal value (call as function)// services/user.service.ts
import { Injectable, signal, computed } from '@angular/core'
import { HttpClient } from '@angular/common/http'
@Injectable({ providedIn: 'root' })
export class UserService {
private users = signal<User[]>([])
private currentUser = signal<User | null>(null)
// Public computed signals
readonly userCount = computed(() => this.users().length)
readonly isAuthenticated = computed(() => this.currentUser() !== null)
constructor(private http: HttpClient) {}
async fetchUsers() {
const users = await this.http.get<User[]>('/api/users').toPromise()
this.users.set(users)
}
setCurrentUser(user: User) {
this.currentUser.set(user)
}
}
// Component usage
export class MyComponent {
constructor(public userService: UserService) {}
// Access in template
// {{ userService.userCount() }}
}
// app.routes.ts
import { Routes } from '@angular/router'
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component').then(m => m.HomeComponent)
},
{
path: 'users/:id',
loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent)
}
]
// Component with route params
import { Component } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
export class UserDetailComponent {
userId = signal<string>('')
constructor(
private route: ActivatedRoute,
private router: Router
) {
this.userId.set(this.route.snapshot.paramMap.get('id') ?? '')
}
goBack() {
this.router.navigate(['/'])
}
}
import { Component, signal } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { toSignal } from '@angular/core/rxjs-interop'
export class UserListComponent {
private http = inject(HttpClient)
// Convert Observable to Signal
users = toSignal(this.http.get<User[]>('/api/users'), { initialValue: [] })
// Or manual signal management
manualUsers = signal<User[]>([])
async loadUsers() {
const users = await this.http.get<User[]>('/api/users').toPromise()
this.manualUsers.set(users)
}
}
loadComponent for routesFormGroup<T> for type safety| Pattern | React | Vue 3 | Svelte 5 | Angular |
|---|---|---|---|---|
| Local State | useState | ref() | $state() | signal() |
| Derived State | useMemo | computed() | $derived | computed() |
| Side Effects | useEffect | watch/watchEffect | $effect() | effect() |
| Global State | Zustand/Context | Pinia | Stores | Services |
| Async State | TanStack Query | VueQuery/Composables | Stores | RxJS/Signals |
Props/Events Pattern (All frameworks):
Slots/Children Pattern:
// React
<Layout>
<Header />
<Content />
</Layout>
// Vue
<Layout>
<template #header><Header /></template>
<template #content><Content /></template>
</Layout>
// Svelte
<Layout>
<Header slot="header" />
<Content slot="content" />
</Layout>
// Angular
<app-layout>
<app-header header></app-header>
<app-content content></app-content>
</app-layout>
File-Based Routing:
[id] for dynamic segmentsProgrammatic Routing:
Universal Techniques:
Framework-Specific:
React.memo, useMemo, useCallback, Suspensev-memo, shallowRef, markRaw, KeepAlive$derived, minimal runtimeUI/Design:
Backend Integration:
Full-Stack Frameworks:
Development:
Skill Status: Multi-framework coverage with progressive disclosure