> ## Documentation Index
> Fetch the complete documentation index at: https://www.1password.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tutorials

> Step-by-step tutorials for 1Password developer tools. Filter by tool and topic to find what you need.

export const TutorialExplorer = () => {
  const [statics] = useState(() => {
    const bodyText = "text-[var(--op-text-body)] dark:text-[var(--op-text-body-dark)]";
    const accentText = "text-[var(--op-secure-blue)] dark:text-[var(--op-cornflower)]";
    const TUTORIALS = [{
      title: "Developer tools quickstart",
      desc: "Set up 1Password CLI, SSH, and your first Environment.",
      href: "/get-started/developer-quickstart",
      icon: "code-commit",
      topic: "Get started",
      tools: ["CLI", "SSH", "Environments"]
    }, {
      title: "Set up the JavaScript SDK with a service account",
      desc: "Build an application that securely fetches a secret from your account.",
      href: "/sdks/setup-tutorial",
      icon: "rocket",
      topic: "Build with 1Password",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Fetch an API key and authenticate",
      desc: "Retrieve an API key at runtime and authenticate to a service like Twilio.",
      href: "https://github.com/SimonBarendse/1password-sdks-rotation-demo/tree/main/message-service",
      icon: "key",
      topic: "Build with 1Password",
      tools: ["SDKs"]
    }, {
      title: "Build an inventory tracker with the JavaScript SDK",
      desc: "Use 1Password as the secure backend for an inventory tracker web app.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-inventory-tracker-webapp",
      icon: "server",
      topic: "Build with 1Password",
      tools: ["SDKs"]
    }, {
      title: "Use service accounts with 1Password CLI",
      desc: "Authenticate 1Password CLI with a service account token, and see which commands it supports.",
      href: "/service-accounts/use-with-1password-cli",
      icon: "square-terminal",
      topic: "Build with 1Password",
      tools: ["CLI", "Service Accounts"]
    }, {
      title: "Create and share items with an Azure Function",
      desc: "Create items and share links from an Azure Function with the Python SDK.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-azure-function-create-item-and-share",
      icon: "bolt",
      topic: "Build with 1Password",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Build a web app to create new items in a vault",
      desc: "Collect credentials into a vault with a Flask web form and the Python SDK.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-reverse-sharing",
      icon: "inbox",
      topic: "Build with 1Password",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Secure note sharing script",
      desc: "Create secure notes with file attachments and share links using the Python SDK.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-share-script",
      icon: "paperclip",
      topic: "Build with 1Password",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Use 1Password CLI with a Connect server",
      desc: "Provision secrets and retrieve items on the command line with a Connect server.",
      href: "/connect/cli",
      icon: "terminal",
      topic: "Build with 1Password",
      tools: ["CLI", "Connect"]
    }, {
      title: "Create encrypted vault backups",
      desc: "Back up and restore vaults with encrypted exports using the JavaScript SDK and CLI.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-vault-backup-webapp",
      icon: "box-archive",
      topic: "Manage vaults & items",
      tools: ["SDKs", "CLI", "Service Accounts"]
    }, {
      title: "Create items with the CLI",
      desc: "Create new items from your terminal with 1Password CLI.",
      href: "/cli/item-create",
      icon: "plus",
      topic: "Manage vaults & items",
      tools: ["CLI"]
    }, {
      title: "Edit items with the CLI",
      desc: "Update existing items from your terminal with 1Password CLI.",
      href: "/cli/item-edit",
      icon: "pen",
      topic: "Manage vaults & items",
      tools: ["CLI"]
    }, {
      title: "Conceal custom fields in bulk",
      desc: "Flip custom fields to concealed across your vaults with the Python SDK and CLI.",
      href: "https://github.com/1Password/solutions/tree/main/1password/item-management/sdk-conceal-fields",
      icon: "eye-slash",
      topic: "Manage vaults & items",
      tools: ["SDKs", "CLI", "Service Accounts"]
    }, {
      title: "Build a web app to migrate vaults between 1Password accounts",
      desc: "Migrate vaults between 1Password accounts with the JavaScript SDK and CLI.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-vault-migration-webapp",
      icon: "arrow-right-arrow-left",
      topic: "Manage vaults & items",
      tools: ["SDKs", "CLI", "Service Accounts"]
    }, {
      title: "Create, update, and delete items in bulk",
      desc: "Scripts that select items by vault, tag, or field value to update website fields, convert field types, and share items.",
      href: "https://github.com/1Password/solutions/tree/main/1password/item-management",
      icon: "layer-group",
      topic: "Manage vaults & items",
      tools: ["CLI"]
    }, {
      title: "Reverse sharing with Connect",
      desc: "Create Login items through a web form using the Connect REST API.",
      href: "https://github.com/1Password/solutions/tree/main/1password/connect/reverse-sharing",
      icon: "network-wired",
      topic: "Manage vaults & items",
      tools: ["Connect"]
    }, {
      title: "Use 1Password to securely provide credentials to Browserbase",
      desc: "Let Browserbase authenticate without ever seeing your credentials.",
      href: "/agentic-autofill",
      icon: "robot",
      topic: "Secure AI access",
      tools: ["AI"]
    }, {
      title: "Use the MCP Server",
      desc: "Let MCP clients manage 1Password Environments through secure prompts.",
      href: "/environments/mcp-server",
      icon: "plug",
      topic: "Secure AI access",
      tools: ["AI", "Environments"]
    }, {
      title: "Use Cursor with 1Password Environments",
      desc: "Install the 1Password plugin for Cursor to validate locally mounted .env files, connect to the MCP server, and give Cursor Agent the Environments workflow.",
      href: "/security-for-ai/cursor-plugin",
      icon: "puzzle-piece",
      topic: "Secure AI access",
      tools: ["AI", "Environments"]
    }, {
      title: "Integrate SDKs with AI agents",
      desc: "Give AI agents secure, scoped access to secrets using the SDKs.",
      href: "/sdks/ai-agent",
      icon: "microchip",
      topic: "Secure AI access",
      tools: ["SDKs", "AI"]
    }, {
      title: "Validate local .env files with the agent hook",
      desc: "Check that .env files from 1Password Environments are set up before AI agents run commands.",
      href: "/environments/agent-hook-validate",
      icon: "file-shield",
      topic: "Secure AI access",
      tools: ["Environments", "AI"]
    }, {
      title: "Get started with SSH",
      desc: "Set up 1Password as your SSH agent for authentication.",
      href: "/ssh/get-started",
      icon: "key",
      topic: "SSH & Git",
      tools: ["SSH"]
    }, {
      title: "Sign Git commits",
      desc: "Sign Git commits with an SSH key that never leaves 1Password.",
      href: "/ssh/git-commit-signing",
      icon: "signature",
      topic: "SSH & Git",
      tools: ["SSH"]
    }, {
      title: "Manage SSH keys with 1Password CLI",
      desc: "Generate and retrieve SSH keys from your terminal with 1Password CLI.",
      href: "/cli/ssh-keys",
      icon: "fingerprint",
      topic: "SSH & Git",
      tools: ["CLI", "SSH"]
    }, {
      title: "Use the SSH agent with WSL",
      desc: "Use the 1Password SSH agent from Windows Subsystem for Linux.",
      href: "/ssh/integrations/wsl",
      icon: "windows",
      topic: "SSH & Git",
      tools: ["SSH"]
    }, {
      title: "Check your SSH agent setup",
      desc: "Diagnose your Mac or Linux setup with a script that verifies 1Password is your SSH agent.",
      href: "https://github.com/1Password/solutions/blob/main/1password/dev-onboarding-guide/ssh-checker-script.md",
      icon: "stethoscope",
      topic: "SSH & Git",
      tools: ["SSH"]
    }, {
      title: "Load secrets from Environments",
      desc: "Get secrets stored in 1Password Environments using the SDKs and CLI.",
      href: "/environments/read-environment-variables",
      icon: "at",
      topic: "Load secrets",
      tools: ["CLI", "SDKs", "Environments"]
    }, {
      title: "Access secrets through locally mounted .env files",
      desc: "Make your 1Password Environments available as locally mounted .env files.",
      href: "/environments/local-env-file",
      icon: "folder-tree",
      topic: "Load secrets",
      tools: ["Environments"]
    }, {
      title: "Automatically authenticate CLI tools with the correct account using shell plugins",
      desc: "Set directory-specific defaults so shell plugins use your work or personal credentials automatically.",
      href: "/cli/shell-plugins/multiple-accounts",
      icon: "users",
      topic: "Load secrets",
      tools: ["CLI", "Shell Plugins"]
    }, {
      title: "Secure CI/CD and deployments with 1Password",
      desc: "Load secrets into CI/CD pipelines, containers, and production apps at runtime without plaintext credentials in your configs.",
      href: "/get-started/secure-deployment",
      icon: "cloud",
      topic: "CI/CD & deployments",
      tools: ["CLI", "Service Accounts", "Environments"]
    }, {
      title: "Get started with 1Password CI/CD Integrations",
      desc: "Securely access secrets in your CI/CD pipeline without exposing plaintext credentials in code.",
      href: "/ci-cd",
      icon: "gears",
      topic: "CI/CD & deployments",
      tools: ["CLI", "Service Accounts", "Connect"]
    }, {
      title: "Get started with service accounts",
      desc: "Create a service account and get a token so automated workflows can access your vaults.",
      href: "/service-accounts/get-started",
      icon: "id-badge",
      topic: "CI/CD & deployments",
      tools: ["Service Accounts"]
    }, {
      title: "Rotate an API key on a schedule",
      desc: "Automate credential rotation with the SDKs and AWS EventBridge.",
      href: "https://github.com/SimonBarendse/1password-sdks-rotation-demo/tree/main/rotation-service",
      icon: "arrows-rotate",
      topic: "CI/CD & deployments",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Provision users from a CSV",
      desc: "Invite, suspend, reactivate, or delete users in bulk with a 1Password CLI script.",
      href: "https://github.com/1Password/solutions/tree/main/1password/scripted-provisioning",
      icon: "file-csv",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Add and remove team members",
      desc: "Provision and deprovision users with 1Password CLI.",
      href: "/cli/provision-users",
      icon: "user-plus",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Grant and revoke vault permissions",
      desc: "Manage who can access which vaults with 1Password CLI.",
      href: "/cli/grant-revoke-vault-permissions",
      icon: "lock",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Recover accounts",
      desc: "Recover locked-out user accounts with 1Password CLI.",
      href: "/cli/recover-users",
      icon: "life-ring",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Automate the user lifecycle",
      desc: "Manage users programmatically with the Users API.",
      href: "/users-api/get-started",
      icon: "users-gear",
      topic: "Team & organization",
      tools: ["Users API"]
    }, {
      title: "Onboard employees to Okta",
      desc: "Automate secure employee onboarding to Okta with the SDKs.",
      href: "https://github.com/1Password/solutions/tree/main/1password/onepassword_sdks/demo-share-okta-user-script",
      icon: "user-check",
      topic: "Team & organization",
      tools: ["SDKs", "Service Accounts"]
    }, {
      title: "Create vaults in bulk from a file",
      desc: "Create a vault for each name in a file, and optionally revoke your own access, with a Python script.",
      href: "https://github.com/1Password/solutions/blob/main/1password/account-management/README.md",
      icon: "folder-plus",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Export vault access reports to CSV",
      desc: "Generate CSV reports of who can access which vaults, by direct assignment or group membership, and with what permissions.",
      href: "https://github.com/1Password/solutions/blob/main/1password/reporting/README.md",
      icon: "file-csv",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Find users who haven't signed in recently",
      desc: "List people who haven't authenticated to 1Password for a chosen number of days with a Python or PowerShell script.",
      href: "https://github.com/1Password/solutions/blob/main/1password/user-management/README.md",
      icon: "user-clock",
      topic: "Team & organization",
      tools: ["CLI"]
    }, {
      title: "Get started with the Events API",
      desc: "Send 1Password event data to your SIEM or analytics tools.",
      href: "/events-api/get-started",
      icon: "chart-line",
      topic: "Events & analytics",
      tools: ["Events API"]
    }, {
      title: "Send sign-in events to CloudWatch",
      desc: "Pipe Events API sign-in events into AWS CloudWatch Logs with a Go Lambda.",
      href: "https://github.com/1Password/solutions/tree/main/1password/reporting/beta/golang-lambda-deployment",
      icon: "aws",
      topic: "Events & analytics",
      tools: ["Events API"]
    }, {
      title: 'Add the "Save in 1Password" button',
      desc: "Add a button that lets people save credentials to 1Password.",
      href: "/web/add-1password-button-website",
      icon: "floppy-disk",
      topic: "Web experience",
      tools: ["Web"]
    }];
    const TOPICS = ["Get started", "Secure AI access", "Build with 1Password", "Load secrets", "SSH & Git", "Manage vaults & items", "Team & organization", "CI/CD & deployments", "Events & analytics", "Web experience"];
    const SORTS = ["recommended", "topic", "title"];
    const TOOL_ORDER = ["AI", "Environments", "SSH", "CLI", "SDKs", "Shell Plugins", "Service Accounts", "Connect", "Events API", "Users API", "Web"];
    const TOOL_COUNTS = {};
    TUTORIALS.forEach(t => t.tools.forEach(tool => {
      TOOL_COUNTS[tool] = (TOOL_COUNTS[tool] || 0) + 1;
    }));
    const ALL_TOOLS = [...TOOL_ORDER.filter(tool => (tool in TOOL_COUNTS)), ...Object.keys(TOOL_COUNTS).filter(tool => !TOOL_ORDER.includes(tool)).sort((a, b) => a.localeCompare(b))];
    const FALLBACK_TUTORIALS = [...TUTORIALS.filter(t => t.topic === "Get started"), ...TUTORIALS.filter(t => t.topic !== "Get started")].slice(0, 3);
    const decodeSafe = v => {
      try {
        return decodeURIComponent(v);
      } catch (e) {
        return v;
      }
    };
    const parseHash = () => {
      if (typeof window === "undefined") return null;
      const raw = (window.location.hash || "").replace(/^#/, "");
      if (!raw) return null;
      const params = {};
      raw.split("&").forEach(pair => {
        const i = pair.indexOf("=");
        if (i > 0) params[pair.slice(0, i)] = pair.slice(i + 1);
      });
      const list = (key, valid) => {
        const values = (params[key] || "").split(",").map(v => decodeSafe(v).trim()).filter(v => valid.includes(v));
        return [...new Set(values)];
      };
      const sort = decodeSafe(params.sort || "");
      return {
        q: decodeSafe(params.q || ""),
        topics: list("topics", TOPICS),
        tools: list("tools", ALL_TOOLS),
        sort: SORTS.includes(sort) ? sort : "recommended"
      };
    };
    const writeHash = s => {
      if (typeof window === "undefined" || !window.history || !window.history.replaceState) return;
      const parts = [];
      if (s.q.trim()) parts.push("q=" + encodeURIComponent(s.q.trim()));
      if (s.topics.length > 0) parts.push("topics=" + s.topics.map(encodeURIComponent).join(","));
      if (s.tools.length > 0) parts.push("tools=" + s.tools.map(encodeURIComponent).join(","));
      if (s.sort !== "recommended") parts.push("sort=" + encodeURIComponent(s.sort));
      const hash = parts.join("&");
      const url = window.location.pathname + window.location.search + (hash ? "#" + hash : "");
      window.history.replaceState(window.history.state, "", url);
    };
    const matchesSearch = (t, tokens) => {
      if (tokens.length === 0) return true;
      const hay = `${t.title} ${t.desc} ${t.tools.join(" ")} ${t.topic}`.toLowerCase();
      return tokens.every(tok => hay.includes(tok));
    };
    const Dropdown = ({id, label, options, selected, open, onToggleOpen, onToggle}) => {
      const menuId = `${id}-menu`;
      return <div className="relative">
          <button type="button" onClick={onToggleOpen} aria-expanded={open} aria-haspopup="true" aria-controls={menuId} className={`inline-flex items-center gap-2 rounded-lg border border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] px-3 py-1.5 text-sm font-medium transition-colors hover:bg-[var(--op-icy-blue)] dark:hover:bg-white/5 ${bodyText}`}>
            <span>{label}</span>
            {selected.length > 0 && <span className="inline-flex h-5 min-w-[1.25rem] items-center justify-center rounded-full px-1 text-xs font-semibold bg-[var(--op-secure-blue)] text-white dark:bg-[var(--op-cornflower)] dark:text-[var(--op-iron-ore)]">
                {selected.length}
                <span className="sr-only"> selected</span>
              </span>}
            <Icon icon={open ? "chevron-up" : "chevron-down"} size="12" />
          </button>
          {open && <div id={menuId} role="group" aria-label={label} className="absolute left-0 z-30 mt-1 max-h-72 min-w-[200px] overflow-auto rounded-lg border border-[var(--op-border-card-light)] dark:border-[var(--op-border-card-dark)] py-1 shadow-lg bg-[var(--op-surface-card-light)] dark:bg-[var(--op-surface-card-dark)]">
              {options.map(opt => <label key={opt} className={`flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm hover:bg-[var(--op-icy-blue)] dark:hover:bg-white/5 ${bodyText}`}>
                  <input type="checkbox" checked={selected.includes(opt)} onChange={() => onToggle(opt)} className="accent-[var(--op-secure-blue)] dark:accent-[var(--op-cornflower)]" />
                  <span>{opt}</span>
                </label>)}
            </div>}
        </div>;
    };
    const Chip = ({prefix, label, onRemove}) => <span className={`inline-flex items-center gap-1.5 rounded-full border border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] bg-[var(--op-icy-blue)] dark:bg-white/5 py-1 pl-3 pr-1.5 text-xs font-medium ${bodyText}`}>
        <span>
          <span className="opacity-60">{prefix} </span>
          {label}
        </span>
        <button type="button" onClick={onRemove} aria-label={`Remove filter: ${prefix} ${label}`} className={`inline-flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-[var(--op-secure-blue)] hover:text-white dark:hover:bg-[var(--op-cornflower)] dark:hover:text-[var(--op-iron-ore)] ${bodyText}`}>
          <Icon icon="xmark" size="10" />
        </button>
      </span>;
    const TutorialCard = ({t}) => <a href={t.href} className="community-card no-underline flex flex-col gap-3 p-4">
        <div className="flex items-start justify-between gap-2">
          <span className={accentText}>
            <Icon icon={t.icon} size="20" />
          </span>
          <span className={`text-xs font-semibold ${bodyText}`}>{t.topic}</span>
        </div>
        <div className="flex flex-col gap-1">
          <span className={`font-semibold ${bodyText}`}>{t.title}</span>
          <span className={`text-sm opacity-80 ${bodyText}`}>{t.desc}</span>
        </div>
        <div className="mt-auto flex flex-wrap items-center gap-1.5 pt-1">
          {t.tools.map(tool => <span key={tool} className={`rounded border border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] px-1.5 py-0.5 text-xs opacity-80 ${bodyText}`}>
              {tool}
            </span>)}
        </div>
      </a>;
    return {
      bodyText,
      accentText,
      TUTORIALS,
      TOPICS,
      ALL_TOOLS,
      FALLBACK_TUTORIALS,
      parseHash,
      writeHash,
      matchesSearch,
      Dropdown,
      Chip,
      TutorialCard
    };
  });
  const {bodyText, accentText, TUTORIALS, TOPICS, ALL_TOOLS, FALLBACK_TUTORIALS, parseHash, writeHash, matchesSearch, Dropdown, Chip, TutorialCard} = statics;
  const [q, setQ] = useState("");
  const [topics, setTopics] = useState([]);
  const [tools, setTools] = useState([]);
  const [sort, setSort] = useState("recommended");
  const [openMenu, setOpenMenu] = useState(null);
  useEffect(() => {
    const fromHash = parseHash();
    if (fromHash) {
      setQ(fromHash.q);
      setTopics(fromHash.topics);
      setTools(fromHash.tools);
      setSort(fromHash.sort);
    }
  }, []);
  const apply = partial => {
    const next = {
      q,
      topics,
      tools,
      sort,
      ...partial
    };
    setQ(next.q);
    setTopics(next.topics);
    setTools(next.tools);
    setSort(next.sort);
    writeHash(next);
  };
  const toggle = (value, key, list) => apply({
    [key]: list.includes(value) ? list.filter(v => v !== value) : [...list, value]
  });
  const toggleMenu = id => setOpenMenu(cur => cur === id ? null : id);
  const clearAll = () => apply({
    q: "",
    topics: [],
    tools: []
  });
  const qTokens = q.toLowerCase().split(/\s+/).filter(Boolean);
  const hasFilters = qTokens.length > 0 || topics.length > 0 || tools.length > 0;
  const filtered = TUTORIALS.filter(t => matchesSearch(t, qTokens) && (topics.length === 0 || topics.includes(t.topic)) && (tools.length === 0 || tools.every(x => t.tools.includes(x))));
  const sorted = [...filtered];
  if (sort === "title") {
    sorted.sort((a, b) => a.title.localeCompare(b.title));
  } else if (sort === "topic") {
    sorted.sort((a, b) => TOPICS.indexOf(a.topic) - TOPICS.indexOf(b.topic));
  }
  const chips = [];
  if (q.trim()) chips.push({
    id: "q",
    prefix: "Search:",
    label: `“${q.trim()}”`,
    remove: () => apply({
      q: ""
    })
  });
  topics.forEach(v => chips.push({
    id: `topic-${v}`,
    prefix: "Topic:",
    label: v,
    remove: () => apply({
      topics: topics.filter(x => x !== v)
    })
  }));
  tools.forEach(v => chips.push({
    id: `tool-${v}`,
    prefix: "Tool:",
    label: v,
    remove: () => apply({
      tools: tools.filter(x => x !== v)
    })
  }));
  const renderChips = (clearLabel, extraClass) => <div className={`flex flex-wrap items-center gap-2 ${extraClass || ""}`}>
      {chips.map(c => <Chip key={c.id} prefix={c.prefix} label={c.label} onRemove={c.remove} />)}
      <button type="button" onClick={clearAll} className={`text-sm font-medium underline-offset-2 hover:underline ${accentText}`}>
        {clearLabel}
      </button>
    </div>;
  return <div className="tutorial-explorer not-prose" onKeyDown={e => {
    if (e.key === "Escape") setOpenMenu(null);
  }}>
      {}
      <div className="relative z-20 mb-3 flex flex-wrap items-center gap-2">
        <div className="relative">
          <span className={`pointer-events-none absolute inset-y-0 left-2.5 flex items-center opacity-50 ${bodyText}`}>
            <Icon icon="magnifying-glass" size="13" />
          </span>
          <input type="search" value={q} onChange={e => apply({
    q: e.target.value
  })} placeholder="Search tutorials" aria-label="Search tutorials" className={`w-52 rounded-lg border border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] bg-transparent py-1.5 pl-8 pr-2 text-sm outline-none transition-colors placeholder:opacity-60 focus:border-[var(--op-secure-blue)] dark:focus:border-[var(--op-cornflower)] ${bodyText}`} />
        </div>

        <Dropdown id="topic" label="Topic" options={TOPICS} selected={topics} open={openMenu === "topic"} onToggleOpen={() => toggleMenu("topic")} onToggle={v => toggle(v, "topics", topics)} />
        <Dropdown id="tools" label="Tools" options={ALL_TOOLS} selected={tools} open={openMenu === "tools"} onToggleOpen={() => toggleMenu("tools")} onToggle={v => toggle(v, "tools", tools)} />

        <label className={`ml-auto inline-flex items-center gap-2 text-sm ${bodyText}`}>
          <span className="opacity-70">Sort by</span>
          <select value={sort} onChange={e => apply({
    sort: e.target.value
  })} className={`rounded-lg border border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] bg-transparent px-2 py-1.5 text-sm font-medium ${bodyText}`}>
            <option value="recommended">Recommended</option>
            <option value="topic">Topic</option>
            <option value="title">Title (A–Z)</option>
          </select>
        </label>
      </div>

      {}
      {chips.length > 0 && renderChips("Clear all", "mb-3")}

      {}
      <div className={`mb-4 flex items-center gap-3 text-sm ${bodyText}`}>
        <span>
          Showing <strong>{sorted.length}</strong> of {TUTORIALS.length} tutorials
        </span>
      </div>

      {}
      {openMenu && <div aria-hidden="true" className="fixed inset-0 z-10" onClick={() => setOpenMenu(null)} />}

      {}
      {sorted.length === 0 ? <div className={`rounded-xl border border-dashed border-[var(--op-border-ui)] dark:border-[var(--op-border-ui-dark)] p-8 ${bodyText}`}>
          {}
          <div className="text-center font-medium">No tutorials match your current filters.</div>
          <div className="mt-1 text-center text-sm opacity-80">Try removing one of these:</div>
          {renderChips("Clear all filters", "mt-3 justify-center")}
          <div className="mt-8 mb-3 text-center text-sm font-semibold">Or get started with one of these:</div>
          <div className="grid grid-cols-1 gap-4 text-left sm:grid-cols-2 lg:grid-cols-3">
            {FALLBACK_TUTORIALS.map(t => <TutorialCard key={t.href} t={t} />)}
          </div>
        </div> : <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
          {sorted.map(t => <TutorialCard key={t.href} t={t} />)}
        </div>}
    </div>;
};

Task-oriented tutorials for 1Password developer tools. Filter by topic and tool to find the right guide.

<TutorialExplorer />


## Related topics

- [1Password SDK tutorials and examples](/sdks/tutorials.md)
- [Tutorial: Integrate 1Password SDKs with AI agents](/sdks/ai-agent.md)
- [Tutorial: Get started with 1Password SDKs and 1Password Service Accounts](/sdks/setup-tutorial.md)
