Back to portfolio
Political & Business ConsultingMarketing Website

Professional Consulting of Louisiana LLC

A Next.js 15 monorepo for a political and business consulting firm: a customer-facing frontend, branded Sanity Studio CMS, shared React component library, and living style guide, all built with TypeScript and deployed to Vercel.

Professional Consulting of Louisiana LLC

Case Study

The biggest challenge on this project wasn't a specific feature. It was setting up the architecture so that design decisions could live in one place that the frontend, CMS, and style guide all pulled from. Getting that dependency graph right, and making Vercel build each app in the correct order, took more planning than any of the visible features did. The result is a codebase where adding a new page or section means composing from an established system, not reinventing conventions.

Technical Narrative

The brief was a professional website for a consulting firm covering political consulting, business consulting, social media management, and web design. The scope was clear enough, but the requirements pointed in a specific direction: the client needed to manage content independently, the design had to be consistent across a potentially growing set of pages, and the site had to perform well in search.

I chose a monorepo structure with Yarn Workspaces from the start. The core reasoning was that if I was going to build a component library and a style guide alongside the frontend, they needed to share a single source of truth for design tokens, components, and configuration. A flat repo would have led to duplicated Tailwind configs and component implementations within weeks of launch. The monorepo made that duplication structurally impossible.

The component library (@proconsults/component-library) ended up with over 50 production-ready components across every category: inputs, cards, navigation, data display, feedback, layout, and specialized brand components. It uses Radix UI primitives for accessibility guarantees, Framer Motion for animations, and React Hook Form with Zod for form handling. The library builds to both ESM and CJS with full TypeScript declarations, and the client and server exports are split so server components in the Next.js frontend don't accidentally pull in client-only code.

For the CMS, I built a fully branded Sanity Studio with custom input components (a visual FontAwesome icon selector, section visibility toggles with warning states, singleton document enforcement) and a built-in documentation system so the client doesn't need to leave the studio to remember how something works. Content types cover everything from homepage sections and service pages to testimonials, FAQs, and SEO metadata, all modeled with validation rules and proper reference relationships.

The frontend uses Next.js 15 App Router with static generation for all primary pages and a webhook-triggered revalidation strategy so content updates go live without a full rebuild. I implemented a multi-layer caching strategy: LRU cache on Sanity queries, React Query for client-side state, and SWR patterns throughout. Structured data for every content type is generated server-side and injected as JSON-LD.

The style guide is its own Next.js app that imports directly from the component library. It documents colors, typography, logo variants with clear space and minimum size rules, buttons, cards, forms, navigation, accessibility tools, and data visualization, all as live interactive examples rather than static screenshots. The docs can never be out of sync with the code.

Challenges

Monorepo build ordering on Vercel. Vercel deploys each app independently, but the component library has to be built first since the apps depend on it. The standard Vercel workspace detection doesn't handle this automatically. I wrote custom vercel-build scripts for each app that explicitly build the library as a prerequisite. It's a sharp edge that took time to diagnose the first time a deployment came back with a missing module error.

Sanity singleton document enforcement. Several content types (homepage, global SEO settings, company info) are singletons. Sanity doesn't enforce this by default. I implemented custom document actions that restrict singleton documents to edit, publish, unpublish, and discard changes only, and configured newDocumentOptions to remove them from the new document menu. It required working through Sanity's internals documentation, but the result is a CMS that's correct by default.

Component library server/client split. Next.js App Router requires strict separation between server and client components. I set up separate entry points (server.ts, client.ts, index.ts) with explicit exports for each context, built as distinct bundles via tsup. A few components that seemed server-safe turned out to pull in client dependencies transitively, which I caught during integration testing and resolved with dynamic imports.

One thing I'd approach differently: the Vercel build configuration. Each app builds the component library fresh on every deployment. For a larger project I'd set up Turborepo with remote caching so that step only runs when the library actually changes.