Use when auditing, scaffolding, or validating MetaSaver React portal app directory structure. Includes file organization patterns, domain grouping, feature composition, routing configuration, and Auth0 integration setup. File types: .tsx, .ts, directory layouts.
Validates MetaSaver React portal directory structure for scaffolding and audits. Ensures domain-grouped features, thin page wrappers, and correct Auth0/api-client placement.
/plugin marketplace add metasaver/claude-marketplace/plugin install core-claude-plugin@metasaver-marketplaceThis skill is limited to using the following tools:
INDEX.mdTEMPLATES.mdreference.mdtemplates/api-client.ts.templatetemplates/app.tsx.templatetemplates/auth-config.ts.templatetemplates/config-index.tsx.templatetemplates/feature-component.tsx.templatetemplates/index.css.templatetemplates/main.tsx.templatetemplates/page-wrapper.tsx.templatetemplates/route-types.ts.templatetemplates/routes.tsx.templateThis skill documents the complete file and folder organization for MetaSaver React portal applications (admin-portal, service-catalog, datafeedr). It ensures consistent patterns across:
Use when:
These are handled by specialized config agents - reference them:
├── vite.config.ts # (vite-agent) Vite build config
├── tsconfig.json # (typescript-configuration-agent) Base TS config
├── tsconfig.app.json # (typescript-configuration-agent) App-specific
├── tsconfig.node.json # (typescript-configuration-agent) Node config
├── tailwind.config.ts # (tailwind-agent) Tailwind setup
├── postcss.config.js # (postcss-agent) PostCSS config
├── eslint.config.js # (eslint-agent) ESLint config
├── package.json # (root-package-json-agent) Dependencies
├── index.html # Entry HTML file
└── .env.example # Environment variable template
public/
└── favicon.svg # Browser tab icon ONLY
# Always place full logo in src/assets/logo.svg
Rule: public/ contains favicon.svg for browser tab. Logo goes in src/assets/logo.svg.
src/
├── assets/
│ └── logo.svg # Full logo for in-app use (NOT favicon)
│
├── config/
│ ├── index.tsx # siteConfig, menuItems exports
│ └── auth-config.ts # Auth0 configuration (MUST be in config/, NOT lib/)
│
├── hooks/ # (optional) App-wide hooks only (e.g., impersonation)
│ └── use-impersonation.ts # Named export pattern
│
├── lib/
│ └── api-client.ts # Axios client with auth token injection ONLY
│
├── features/
│ └── {domain}/ # Grouped by domain (mirrors pages/)
│ └── {feature}/
│ ├── {feature}.tsx # Main feature component (named export)
│ ├── components/ # (optional) Reusable sub-components
│ │ └── {component}.tsx # Each component in own file
│ ├── hooks/ # (optional) Feature-specific hooks
│ │ └── use-{hook}.ts # Each hook in own file
│ ├── config/ # (optional) Feature-specific config
│ │ └── config.ts # Named exports
│ └── queries/ # (optional) API query functions
│ └── {entity}-queries.ts # Query functions
│
├── pages/
│ └── {domain}/ # Grouped by domain (mirrors features/)
│ └── {page}.tsx # Thin wrapper importing from features/
│ └── theme/
│ └── theme.tsx # Theme component (named export)
│
├── routes/
│ ├── route-types.ts # Type-safe ROUTES constant
│ └── routes.tsx # React Router config with lazy loading
│
├── styles/
│ └── theme-overrides.css # CSS overrides (global)
│
├── app.tsx # Root component with Auth0Provider
├── main.tsx # React entry point (ReactDOM.createRoot)
├── index.css # Tailwind imports (@tailwind directives)
└── vite-env.d.ts # Vite type definitions
IMPORTANT - File structure requirements:
@metasaver/{app}-contracts packages for types - do not create src/types/src/config/ - do not put auth-config.ts in src/lib/Rule 1: Domain Grouping Features are organized by domain to match page structure:
features/
├── microservices-feature/ # Maps to pages/service-catalog/micro-services.tsx
├── endpoints-feature/ # Maps to pages/service-catalog/endpoints.tsx
├── merchants-feature/ # Maps to pages/datafeedr/merchants.tsx
└── status-feature/ # Maps to pages/datafeedr/status.tsx
Rule 2: Use Direct Imports
Always import directly from specific files using #/ alias for internal imports:
// CORRECT - Import from specific files with full paths
import { MicroservicesFeature } from "#/features/microservices-feature/microservices.tsx";
import { useGetServices } from "#/features/microservices-feature/hooks/use-get-services.ts";
import { ServiceCard } from "#/features/microservices-feature/components/service-card.tsx";
Rule 3: Component Naming
microservices.tsxexport function MicroservicesFeature() { ... }components/ subfolder with descriptive namesRule 4: Optional Subfolders
Only create components/, hooks/, config/, queries/ if needed:
queries/ contains API query functions (e.g., {entity}-queries.ts)Rule 1: Mirrors Features Page structure mirrors feature structure by domain:
pages/
├── service-catalog/
│ ├── micro-services.tsx
│ ├── endpoints.tsx
│ └── schedule.tsx
├── datafeedr/
│ ├── merchants.tsx
│ ├── networks.tsx
│ ├── products.tsx
│ └── status.tsx
└── home/
└── home.tsx
Rule 2: Thin Wrappers Page files are thin wrappers (5-15 lines) with direct imports:
// src/pages/service-catalog/micro-services.tsx
import { MicroservicesFeature } from "#/features/microservices-feature/microservices.tsx";
export function MicroServicesPage() {
return <MicroservicesFeature />;
}
Rule 3: Keep Pages Thin
#/ aliasRule 4: Import Order Follow standard import order in all files:
// 1. Node.js built-ins (rare in React apps)
import { resolve } from "path";
// 2. External packages
import { useState, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
// 3. Workspace packages (external imports)
import type { User } from "@metasaver/contracts/users/types";
// 4. Internal imports (same package)
import { MicroservicesFeature } from "#/features/microservices-feature/microservices.tsx";
import { useAuth } from "#/hooks/use-auth.ts";
index.tsx exports:
export const siteConfig = {
name: "Admin Portal",
description: "...",
// ...
};
export const menuItems = [
// Navigation structure
];
auth-config.ts exports:
export const auth0Config = {
domain: process.env.VITE_AUTH0_DOMAIN,
clientId: process.env.VITE_AUTH0_CLIENT_ID,
// ...
};
route-types.ts defines type-safe routes:
export const ROUTES = {
HOME: "/",
DASHBOARD: "/dashboard",
MICROSERVICES: "/service-catalog/microservices",
// ...
} as const;
routes.tsx uses lazy loading with named exports:
import { lazy } from "react";
import { createBrowserRouter } from "react-router-dom";
const HomePage = lazy(() =>
import("#/pages/home/home.tsx").then((m) => ({
default: m.HomePage,
}))
);
const MicroServicesPage = lazy(() =>
import("#/pages/service-catalog/micro-services.tsx").then((m) => ({
default: m.MicroServicesPage,
}))
);
export const router = createBrowserRouter([
{
path: ROUTES.HOME,
element: <HomePage />,
},
{
path: ROUTES.MICROSERVICES,
element: <MicroServicesPage />,
},
// ...
]);
theme-overrides.css contains:
Does NOT contain:
Create Feature Directory
mkdir -p src/features/{domain}/{feature}
mkdir -p src/features/{domain}/{feature}/components
mkdir -p src/features/{domain}/{feature}/hooks
mkdir -p src/features/{domain}/{feature}/config
Create Feature Files (use templates)
src/features/{domain}/{feature}/{feature}.tsx - Main feature component with named exportsrc/features/{domain}/{feature}/components/{component}.tsx - Component files as neededsrc/features/{domain}/{feature}/hooks/use-{hook}.ts - Hook files as neededsrc/features/{domain}/{feature}/config/config.ts - Config file as neededCreate Page File (use template)
src/pages/{domain}/{page}.tsx - Import feature using #/ aliasUpdate Routes
src/routes/route-types.tssrc/routes/routes.tsx with lazy loadingmenuItems in src/config/index.tsxEnsure Import Order
#/)src/ folder existsassets, config, lib, features, pages, routes, styleshooks/ (only for app-wide hooks like impersonation)src/types/ foldersrc/ clean - only have app.tsx, main.tsx, index.css, vite-env.d.tspublic/ contains only favicon.svg (place full logo in src/assets/logo.svg)features/{domain}/{feature}/{feature}.tsx in feature root with named exportcomponents/ with descriptive names (if exists)hooks/ folder with use- prefixindex.ts barrel filesmicroservices-feature#/ alias for internal pathspages/{domain}/{page}.tsx#/ aliasexport function {Page}Page() (named export only)#/)src/config/index.tsx exports siteConfig and menuItemssrc/config/auth-config.ts exports auth0Config objectsrc/lib/api-client.ts exports configured Axios instancesrc/routes/route-types.ts defines ROUTES constantROUTES are type-safesrc/routes/routes.tsx uses lazy loading for pagessrc/styles/theme-overrides.css contains only overridessrc/index.css@tailwind directives present in src/index.csspostcss.config.jssrc/app.tsx wraps with Auth0Providersrc/main.tsx uses ReactDOM.createRoot()src/index.css imports Tailwindsrc/vite-env.d.ts declares Vite typesindex.html references src/main.tsxsrc/assets/logo.svg contains full logopublic/favicon.svg contains browser tab icon ONLYViolation: Pages contain business logic
// INCORRECT
export function MicroServicesPage() {
const [services, setServices] = useState([]);
useEffect(() => { /* fetch logic */ }, []);
return <ServiceTable services={services} />;
}
Fix: Move logic to feature component
// CORRECT - Page wrapper
export function MicroServicesPage() {
return <MicroservicesFeature />;
}
// Feature component has logic
export function MicroservicesFeature() {
const [services, setServices] = useState([]);
useEffect(() => { /* fetch logic */ }, []);
return <ServiceTable services={services} />;
}
Violation: Using barrel exports (index.ts files)
// INCORRECT - Barrel export pattern
src/features/microservices-feature/
├── index.ts // export * from "./microservices"
├── microservices.tsx
├── hooks/
│ ├── index.ts // export * from "./use-get-services"
│ └── use-get-services.ts
// INCORRECT - Import from barrel
import { MicroservicesFeature } from "#/features/microservices-feature";
Fix: Use direct imports with #/ alias
// CORRECT - No index.ts files, use direct imports
src/features/microservices-feature/
├── microservices.tsx
└── hooks/
└── use-get-services.ts
// CORRECT - Direct import with full path
import { MicroservicesFeature } from "#/features/microservices-feature/microservices.tsx";
import { useGetServices } from "#/features/microservices-feature/hooks/use-get-services.ts";
Violation: Page structure doesn't mirror feature structure
features/microservices-feature/ pages/dashboard/
pages/admin/microservices.tsx ✗ (mismatch in domain/path)
Fix: Align domain structure
features/service-catalog/microservices-feature/
pages/service-catalog/micro-services.tsx ✓
Violation: Config file hardcoding values
// INCORRECT
export const auth0Config = {
domain: "dev-abc123.auth0.com",
clientId: "xyz789",
};
Fix: Use environment variables
// CORRECT
export const auth0Config = {
domain: import.meta.env.VITE_AUTH0_DOMAIN,
clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
};
# Commands to validate structure
find src -type f -name "*.tsx" -o -name "*.ts" | head -20
ls -la src/features/
ls -la src/pages/
Audit checklist:
index.tsFiles to create:
src/features/datafeedr/products-feature/index.tssrc/features/datafeedr/products-feature/products.tsxsrc/features/datafeedr/products-feature/hooks/index.tssrc/features/datafeedr/products-feature/hooks/use-get-products.tssrc/pages/datafeedr/products.tsxsrc/routes/route-types.tssrc/routes/routes.tsxsrc/config/index.tsx menuItemsUse templates for each file.
// Check that pages import from matching features using #/ alias
// src/pages/service-catalog/micro-services.tsx
import { MicroservicesFeature } from "#/features/service-catalog/microservices-feature/microservices.tsx";
// Domain must match: service-catalog / service-catalog ✓
// Feature path must match: microservices-feature / microservices-feature ✓
// Import uses #/ alias ✓
// Import includes full file path with extension ✓