--- name: polaris-ui-patterns description: Build consistent UI using Polaris Web Components patterns for common page layouts like index pages, detail pages, forms, and empty states. Use this skill when creating new pages or refactoring existing UI components. allowed-tools: [Read, Edit, Write, Grep, Glob] --- # Polaris UI Patterns Skill ## Purpose This skill provides reusable UI patterns and templates for common page layouts in Shopify apps using Polaris Web Components. ## When to Use This Skill - Creating new pages (index, detail, form) - Implementing common UI patterns - Building consistent layouts - Adding empty states - Creating modals and forms - Implementing tables with actions ## Core Patterns ### Pattern 1: Index/List Page **Use for**: Products, Orders, Customers, Custom Entities ```tsx import type { LoaderFunctionArgs } from "react-router"; import { useLoaderData } from "react-router"; import { authenticate } from "../shopify.server"; import { db } from "../db.server"; import { json } from "@remix-run/node"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { session } = await authenticate.admin(request); const shop = await db.shop.findUnique({ where: { shopDomain: session.shop } }); const items = await db.item.findMany({ where: { shopId: shop.id }, orderBy: { createdAt: 'desc' }, take: 50, }); const stats = { total: items.length, active: items.filter(i => i.isActive).length, }; return json({ items, stats }); }; export default function ItemsIndexPage() { const { items, stats } = useLoaderData(); return ( {/* Stats Section */} Total Items {stats.total} Active {stats.active} {/* Table Section */} Name Status Created Actions {items.map(item => ( {item.name} {item.isActive ? "Active" : "Inactive"} {new Date(item.createdAt).toLocaleDateString()} Edit Delete ))} ); } ``` ### Pattern 2: Detail/Edit Page ```tsx export const loader = async ({ params, request }: LoaderFunctionArgs) => { const { session } = await authenticate.admin(request); const shop = await db.shop.findUnique({ where: { shopDomain: session.shop } }); const item = await db.item.findUnique({ where: { id: params.id, shopId: shop.id, }, }); if (!item) { throw new Response("Not Found", { status: 404 }); } return json({ item }); }; export const action = async ({ params, request }: ActionFunctionArgs) => { const formData = await request.formData(); const name = formData.get("name"); const description = formData.get("description"); await db.item.update({ where: { id: params.id }, data: { name, description }, }); return redirect("/app/items"); }; export default function ItemDetailPage() { const { item } = useLoaderData(); return (
Save Cancel
); } ``` ### Pattern 3: Modal Pattern ```tsx function ItemModal({ item, onClose }) { const submit = useSubmit(); function handleSubmit(event) { event.preventDefault(); const formData = new FormData(event.target); submit(formData, { method: "post" }); onClose(); } return (
Cancel Save
); } ``` ### Pattern 4: Empty State ```tsx {items.length === 0 ? ( Start by adding your first item Add Item ) : ( // Item list )} ``` ### Pattern 5: Loading State ```tsx import { useNavigation } from "@remix-run/react"; export default function ItemsPage() { const navigation = useNavigation(); const isLoading = navigation.state === "loading"; return ( {isLoading ? ( ) : ( // Content )} ); } ``` ### Pattern 6: Form with Validation ```tsx export const action = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const name = formData.get("name"); const errors = {}; if (!name) errors.name = "Name is required"; if (name.length < 3) errors.name = "Name must be at least 3 characters"; if (Object.keys(errors).length > 0) { return json({ errors }, { status: 400 }); } await db.item.create({ data: { name } }); return redirect("/app/items"); }; export default function NewItemPage() { const actionData = useActionData(); return (
Save ); } ``` ## Best Practices 1. **Consistent Layouts** - Use the same page structure across the app 2. **Loading States** - Always show skeleton loaders during data fetching 3. **Empty States** - Provide clear guidance when no data exists 4. **Error Handling** - Show user-friendly error messages 5. **Form Validation** - Validate on submit, show inline errors 6. **Responsive Design** - Test on mobile, tablet, and desktop 7. **Accessibility** - Use semantic HTML and ARIA attributes 8. **SSR Compatibility** - Avoid hydration mismatches 9. **Performance** - Lazy load components when appropriate 10. **User Feedback** - Show success/error toasts after actions --- **Remember**: Consistent UI patterns create a professional, predictable user experience.