sadmann7/skateshop

An open source e-commerce skateshop build with everything new in Next.js.

5,483 stars TypeScript 6 components

Multi-tenant e-commerce platform where users create skateboard shops with inventory management

Users register and authenticate through Clerk, then create stores with configurable subscription plans. Store owners add products with images uploaded via UploadThing, categorize inventory, and manage orders through the dashboard. Customer-facing storefronts display products filtered by category and availability, while the admin interface handles inventory updates, plan limit enforcement, and Stripe payment processing.

Under the hood, the system uses 2 feedback loops, 3 data pools, 3 control points to manage its runtime behavior.

A 6-component fullstack. 314 files analyzed. Data flows through 6 distinct pipeline stages.

How Data Flows Through the System

Users register and authenticate through Clerk, then create stores with configurable subscription plans. Store owners add products with images uploaded via UploadThing, categorize inventory, and manage orders through the dashboard. Customer-facing storefronts display products filtered by category and availability, while the admin interface handles inventory updates, plan limit enforcement, and Stripe payment processing.

  1. User Authentication — Clerk handles user registration, login, and session management, providing user objects with unique IDs that link to store ownership
  2. Store Registration — CreateStoreDialog component validates against plan limits, generates unique store IDs using generateId(), and inserts new Store records with default free plan settings [User → Store]
  3. Product Creation — Dashboard forms collect product details, validate against store limits, upload images via UploadThing to get StoredFile objects, then insert Product records with decimal pricing and integer inventory [Store → Product]
  4. Image Upload Processing — UploadThing middleware handles file validation and CDN upload, returning StoredFile objects with permanent URLs that get stored as JSON in product images columns [File Upload → StoredFile]
  5. Storefront Display — Server components query products by store ID and status, filter by categories and subcategories, and render product cards with images from StoredFile URLs [Product → Product Display]
  6. Dashboard Navigation — DashboardSidebar reads current route segments, matches against NavItem definitions, and renders active states while StoreSwitcher manages multi-store context switching [NavItem → Navigation UI]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

Store src/db/schema/stores.ts
table with id: varchar(30), userId: varchar(36), slug: text, name: text, plan: storePlanEnum('free'|'standard'|'pro'), planEndsAt: timestamp, productLimit: integer, stripeAccountId: varchar
Created when user registers a new store, updated when plan changes, referenced by all store-related operations
Product src/db/schema/products.ts
table with id: varchar(30), name: text, description: text, images: json(StoredFile[]), categoryId: varchar(30), price: decimal(10,2), inventory: integer, status: productStatusEnum('active'|'draft'|'archived'), storeId: varchar(30)
Created by store owners, updated through dashboard, displayed on storefront, archived when discontinued
StoredFile src/types/index.ts
interface with id: string, name: string, url: string extending ClientUploadedFileData
Created during file upload via UploadThing, stored as JSON in product images, served from CDN URLs
NavItem src/types/index.ts
interface with title: string, href?: string, active?: boolean, disabled?: boolean, external?: boolean, icon?: keyof Icons, label?: string, description?: string
Defined statically in components, dynamically constructed based on user permissions and current route
Plan src/types/index.ts
interface with id: Store['plan'], title: string, description: string, features: string[], stripePriceId: string, limits: {stores: number, products: number, tags: number, variants: number}
Defined as configuration, enforced during store operations, updated when user changes subscription

Hidden Assumptions

Things this code relies on but never validates. These are the things that cause silent failures when the system changes.

critical Contract unguarded

DATABASE_URL environment variable contains a valid PostgreSQL connection string that stays valid for the application lifetime

If this fails: If the connection string becomes invalid (credentials expire, server moves, network changes), all database operations fail with connection errors but the application continues running with broken state

src/db/index.ts:postgres
warning Ordering unguarded

Route segments array from useSelectedLayoutSegments() has predictable structure where segments[0] is the primary section and segments.includes() accurately reflects nested routes

If this fails: If Next.js changes segment ordering or includes unexpected segments (like dynamic route parameters), navigation highlights wrong sections or shows multiple active states simultaneously

src/app/(dashboard)/store/[storeId]/_components/dashboard-sidebar.tsx:useSelectedLayoutSegments
critical Contract weakly guarded

useSidebar hook is only called within components wrapped by SidebarProvider in the component tree

If this fails: Calling useSidebar outside the provider throws runtime error 'useSidebar must be used within a SidebarProvider', crashing the component that tries to access sidebar state

src/components/layouts/sidebar-provider.tsx:useSidebar
critical Domain unguarded

StoredFile.url contains a permanently accessible CDN URL that will remain valid indefinitely without authentication or expiration

If this fails: If UploadThing CDN URLs expire, change domains, or require authentication, product images break silently - users see broken image placeholders but no error is thrown

src/types/index.ts:StoredFile
critical Contract unguarded

storesPromise and planMetricsPromise are actual Promise objects that will resolve successfully with expected data shapes

If this fails: If promises reject or resolve with unexpected data, React.use() throws unhandled errors that crash the component, making store switching completely unusable

src/app/(dashboard)/store/[storeId]/_components/store-switcher.tsx:React.use
critical Scale unguarded

nanoid with 30-character length provides sufficient uniqueness for all database primary keys across all stores and products without collisions

If this fails: ID collisions cause database constraint violations during insert operations, failing store or product creation with cryptic errors that don't indicate the root cause

src/lib/id.ts:generateId
warning Temporal unguarded

Database server clock and application server clock are synchronized when $onUpdate() executes new Date() versus database current_timestamp default

If this fails: Clock skew causes updatedAt timestamps to be inconsistent - some records show future dates, others lag behind, breaking audit trails and temporal queries

src/db/schema/utils.ts:updatedAt
critical Contract weakly guarded

storeId parameter from URL route [storeId] is always a valid store identifier that matches existing store records in the database

If this fails: Invalid storeId causes selectedStore to be undefined, leading to component render errors when trying to access store properties, making dashboard inaccessible

src/app/(dashboard)/store/[storeId]/_components/store-switcher.tsx:useParams
warning Domain unguarded

stripePriceId corresponds to actual Stripe price objects that exist in the connected Stripe account and remain valid

If this fails: Invalid Stripe price IDs cause subscription upgrades to fail with Stripe API errors, but users see generic error messages without understanding the payment issue

src/types/index.ts:Plan
warning Shape unguarded

JSON-stored StoredFile arrays in product.images column always contain objects with id, name, and url properties

If this fails: Corrupted JSON or missing properties cause runtime errors when components try to render product images, showing TypeScript errors in production instead of fallback images

src/types/index.ts:StoredFile

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

PostgreSQL Database (database)
Primary data store containing all relational tables for stores, products, users, orders, and metadata with foreign key constraints
Sidebar Context State (in-memory)
React Context holding boolean open/close state for mobile sidebar across dashboard pages
UploadThing File Storage (file-store)
CDN-backed file storage for product images with permanent URLs referenced in database JSON fields

Feedback Loops

Delays

Control Points

Technology Stack

Next.js 14 (framework)
App Router framework providing server components, file-based routing, and API routes for the e-commerce application
Drizzle ORM (database)
Type-safe database ORM that generates TypeScript types from schema definitions and handles PostgreSQL queries
Clerk (infra)
Authentication provider handling user registration, login, session management, and user profile data
Stripe (infra)
Payment processing and subscription management for store plans and customer transactions
UploadThing (infra)
File upload service that handles product image storage and returns CDN URLs for database storage
Tailwind CSS (framework)
Utility-first CSS framework for styling components with responsive design and dark mode support
shadcn/ui (library)
React component library built on Radix UI primitives providing accessible and customizable UI components

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Fullstack Repositories

Frequently Asked Questions

What is skateshop used for?

Multi-tenant e-commerce platform where users create skateboard shops with inventory management sadmann7/skateshop is a 6-component fullstack written in TypeScript. Data flows through 6 distinct pipeline stages. The codebase contains 314 files.

How is skateshop architected?

skateshop is organized into 4 architecture layers: Database Schema Layer, Data Access Layer, UI Component Layer, Route Handler Layer. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through skateshop?

Data moves through 6 stages: User Authentication → Store Registration → Product Creation → Image Upload Processing → Storefront Display → .... Users register and authenticate through Clerk, then create stores with configurable subscription plans. Store owners add products with images uploaded via UploadThing, categorize inventory, and manage orders through the dashboard. Customer-facing storefronts display products filtered by category and availability, while the admin interface handles inventory updates, plan limit enforcement, and Stripe payment processing. This pipeline design reflects a complex multi-stage processing system.

What technologies does skateshop use?

The core stack includes Next.js 14 (App Router framework providing server components, file-based routing, and API routes for the e-commerce application), Drizzle ORM (Type-safe database ORM that generates TypeScript types from schema definitions and handles PostgreSQL queries), Clerk (Authentication provider handling user registration, login, session management, and user profile data), Stripe (Payment processing and subscription management for store plans and customer transactions), UploadThing (File upload service that handles product image storage and returns CDN URLs for database storage), Tailwind CSS (Utility-first CSS framework for styling components with responsive design and dark mode support), and 1 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does skateshop have?

skateshop exhibits 3 data pools (PostgreSQL Database, Sidebar Context State), 2 feedback loops, 3 control points, 2 delays. The feedback loops handle circuit-breaker and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does skateshop use?

4 design patterns detected: Multi-tenant SaaS Architecture, Server Components with Promises, Schema-First Database Design, Compound Component Pattern.

Analyzed on April 20, 2026 by CodeSea. Written by .