More Cards Snippets
Integration Cards — Connect Apps Grid HTML CSS JS
Integration Cards · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Integration Cards — App Connection Grid with Connect/Disconnect State

The integrations or "connected apps" page — a grid of cards for Slack, GitHub, Stripe, and the rest, each with a Connect or Disconnect button — is a standard part of every SaaS product's settings. This snippet builds that grid in plain HTML, CSS, and vanilla JavaScript: branded app logos, a clear connected state, category labels, a filter between all and connected apps, and a toggle that flips each card between connect and disconnect.
A card that reflects its connection state
Each integration card shows the app's logo (on its brand color), name, category, and a short description of what connecting does. The card's entire appearance responds to whether it's connected: a connected card gets a green-tinted border and background, a checkmark next to its name, a green "● Connected" status, and its action button becomes a red "Disconnect" — while a disconnected card is neutral with a "Connect" button. This whole-card state change makes it instantly scannable which apps are active, far clearer than a small toggle alone, and it follows the convention users know from GitHub, Slack, and Zapier's app directories.
Connect and disconnect from one toggle
The action button flips the integration's on flag and re-renders, so the same button connects a disconnected app and disconnects a connected one, with its label and styling updating to match. In a real app this is where you'd start the provider's OAuth connect flow (for connecting) or call your disconnect endpoint (for disconnecting) — the demo toggles state locally so the full interaction is visible. Making disconnect a distinct red action (not a quiet toggle) is deliberate: disconnecting often stops data syncing, so it should look like a considered action.
Filtering all vs. connected
A filter switches between showing every available integration and only the connected ones — the two views users actually want ("what can I connect?" and "what's already connected?"). The filter re-renders from the same data, and when "Connected" is selected with nothing connected, an empty state explains why the grid is blank rather than leaving it confusingly empty. This is the standard two-tab pattern for an integrations directory.
Data-driven and category-organized
Every card comes from an INTEGRATIONS array (id, name, category, description, icon, brand color, connected flag), so the catalog is a data edit — add an app, change a description, or mark one connected by editing one entry. Each card's category label ("Communication," "Development," "Payments") helps users scan a long directory; in a larger catalog you'd extend the filter to category tabs, which the same render supports.
Responsive grid, drop-in ready
The cards sit in an auto-fill grid that reflows from two or three columns down to one on narrow screens, so the directory works on any device. Because it's entirely data-driven and self-contained, you populate INTEGRATIONS from your backend (which apps exist and which the current account has connected), wire the buttons to your OAuth and disconnect flows, and the grid renders the rest. The same component works for any "connect external services" surface, not just a settings page.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not have to trace the render and filter interplay entirely on your own. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the render function rebuilds the whole grid's innerHTML from the INTEGRATIONS array on every filter change and every toggle click, or why the same button element is reused for both connecting and disconnecting instead of being two separate buttons. The same assistant can help you optimize it — ask whether rebuilding the entire grid's innerHTML on every single toggle is wasteful once the catalog grows to dozens of integrations, and whether a more targeted DOM update (patching just the toggled card) would scale better. It is just as useful for extending the directory: ask it to add a real OAuth redirect flow in the toggle handler instead of the local on flag flip, a search box that filters INTEGRATIONS by name alongside the existing all/connected chips, or a confirmation dialog before disconnecting an integration that has active automations. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a SaaS integrations directory grid in plain HTML, CSS, and JavaScript, data-driven from a single array — no libraries.
Requirements:
- An INTEGRATIONS array where each entry has an id, display name, category, short description, icon, brand color, and a boolean connected flag.
- A render function that filters the array based on a current filter mode (all integrations, or only connected ones), rebuilds the grid's markup from the filtered list, and shows a distinct empty-state message when the connected filter yields zero results.
- Each rendered card's entire visual state must depend on its connected flag: a connected card gets a tinted border and background, a checkmark badge next to its name, a green "Connected" status label, and its action button reads "Disconnect" styled as a deliberate red/destructive action; a disconnected card is neutral with a "Connect" button.
- A single toggle click handler, delegated at the grid container level, that finds the clicked integration by its id, flips its connected flag, and calls render again — so the same button and same handler both connect and disconnect depending on current state.
- Two filter chip buttons (All, Connected) that update the current filter mode and re-render, with the active chip visually distinguished.
- A responsive grid using CSS grid-template-columns with auto-fill and minmax so cards reflow from multiple columns down to one as the viewport narrows.
- Leave a clear comment in the toggle handler indicating that a real implementation would start an OAuth connect flow or call a disconnect API endpoint at that point, rather than only flipping local state.Step by step
How to Use
- 1Paste HTML, CSS, and JSAn integrations grid renders with apps like Slack, GitHub, and Stripe; connected ones are green-tinted.
- 2Connect an appClick "Connect" on a neutral card — it turns green with a checkmark, a "Connected" status, and a red Disconnect button.
- 3Disconnect an appClick "Disconnect" on a connected card — it returns to the neutral, not-connected state.
- 4Filter the viewSwitch to "Connected" to see only active integrations; an empty state shows if none are connected.
- 5Edit the catalogAdd or change entries in the INTEGRATIONS array (name, category, description, icon, color, on).
- 6Wire up real flowsIn the toggle handler, start the provider's OAuth connect flow or call your disconnect endpoint.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
In the toggle handler, branch on the current state: to connect, start the provider's OAuth flow (redirect to your /connect/slack route or open their OAuth popup), and mark the integration connected on a successful callback; to disconnect, call your backend's disconnect/revoke endpoint and mark it disconnected on success. Show a loading state on the button during the request, and only flip the card's appearance once the operation actually succeeds.
Fetch the account's connected integrations from your backend on load and set each INTEGRATIONS entry's on flag accordingly (match by id). The catalog of available apps can be static or also come from your API; the connected state should always come from the server so it reflects reality across devices and sessions, not just local toggles.
Disconnecting an integration usually stops data syncing or removes access, which can have real consequences (lost automation, broken workflows). Styling it as a deliberate red action — rather than a quiet toggle that looks the same as connecting — signals weight and reduces accidental disconnects. For high-impact integrations, add a confirmation dialog before disconnecting.
Extend the filter from all/connected to category tabs (Communication, Development, Payments…) driven by the cat field, add a search box that filters the INTEGRATIONS array by name, and paginate or lazy-render if the catalog is large. The render is already data-driven, so these are additions to the filtering step, not changes to the card itself.
In React, hold the integrations (with connected flags) and filter in useState, derive the visible list, and call your connect/disconnect API in the toggle handler; in Vue, use a reactive array with a computed filtered list; in Angular, use a component array and a getter. The card rendering and state logic port directly — only the async connect/disconnect calls move into the framework's effects.