Two lines of code to give your LLM eyes.
One attribute. Zero prompt engineering. It knows exactly what the user sees.
askable-promo.mp4
Quick Start · Why · How it works · Works with · Features · Packages · Docs · Agent Templates · Live Demo
npm install @askable-ui/reactWant a runnable Askable + CopilotKit starter app instead?
npx create-askable-app my-app
cd my-app
npm install
npm run devimport { Askable, useAskable } from '@askable-ui/react';
function Dashboard({ kpi }) {
const { promptContext } = useAskable();
// promptContext: "User is focused on: metric=revenue, value=$2.3M, delta=+12%"
return (
<Askable meta={{ metric: kpi.name, value: kpi.value, delta: kpi.delta }}>
<KPICard data={kpi} />
</Askable>
);
}That's it. promptContext updates automatically as the user interacts. Pass it to any LLM.
AI copilots ask users to describe what they're looking at. That's friction — and it's imprecise.
askable-ui solves this with one HTML attribute. Mark any element with data-askable, and the library tracks user focus and serializes it into a prompt-ready string. The model gets the user's exact visual context — not a guess, the real thing.
No page scraping. No DOM serialization. No prompt bloat. Lightweight and zero-dependency.
1. Annotate — same data that renders your chart feeds the AI
<Askable meta={{ metric: 'revenue', value: '$2.3M', delta: '+12%', period: 'Q3' }}>
<RevenueChart data={data} />
</Askable>2. Observe — automatic, zero config
const { ctx, promptContext } = useAskable();
// promptContext updates whenever the user clicks, hovers, or focuses
const { ctx: tableCtx } = useAskable({ name: 'table' });
const { ctx: chartCtx } = useAskable({ name: 'chart' });
// named contexts stay isolated for multi-region pages
const { ctx: screenCtx } = useAskable({ viewport: true });
// screenCtx.toViewportContext() serializes currently visible annotated elements3. Inject — at the AI boundary, one line
const result = await streamText({
model: openai('gpt-4o'),
system: `You are a helpful analytics assistant.\n\n${promptContext}`,
messages,
});LLM SDKs — OpenAI · Anthropic · Vercel AI SDK · CopilotKit · LangChain · any SDK
Frameworks — React · Vue 3 · Svelte · Vanilla JS · Next.js · Nuxt · SvelteKit
askable-ui is the context layer. It doesn't replace your LLM SDK — it gives it eyes.
- Zero config — one attribute, works with any DOM structure, styling system, or component library
- React, Vue, Svelte — idiomatic hooks and components; core is framework-agnostic
- SSR safe — defers to client lifecycle, no
window is not defined - "Ask AI" button —
ctx.select(element)pins focus to any element programmatically - Conversation history —
ctx.toHistoryContext(n)for multi-turn context - Viewport awareness —
ctx.getVisibleElements()/ctx.toViewportContext()for on-screen context - Redaction hooks — strip sensitive fields before data reaches serialization
- Inspector panel —
<AskableInspector />oruseAskable({ inspector: true })for a live dev overlay - Agent templates — reusable
AGENTS.mdguidance and copy-paste prompts for coding-agent-driven adoption - Lightweight core — zero runtime dependencies
| Package | Version | Use when |
|---|---|---|
@askable-ui/core |
Vanilla JS, custom framework, or as a peer dep | |
@askable-ui/react |
React 18+ | |
@askable-ui/react-native |
React Native (initial press-driven adapter) | |
@askable-ui/vue |
Vue 3 | |
@askable-ui/svelte |
Svelte 4 & 5 | |
create-askable-app |
npm package | React + Vite + CopilotKit starter scaffold |
Framework quick starts
npm install @askable-ui/react-nativeimport { Pressable, Text } from 'react-native';
import { Askable, useAskable } from '@askable-ui/react-native';
function RevenueCard() {
const { ctx, promptContext } = useAskable();
return (
<Askable ctx={ctx} meta={{ widget: 'revenue' }} text="Revenue card">
<Pressable>
<Text>Revenue</Text>
</Pressable>
</Askable>
);
}For a runnable mobile demo, see examples/react-native-expo.
npm install @askable-ui/vue<script setup>
import { Askable, useAskable } from '@askable-ui/vue';
const { promptContext } = useAskable();
const props = defineProps(['kpi']);
</script>
<template>
<Askable :meta="{ metric: kpi.name, value: kpi.value }">
<KPICard :data="kpi" />
</Askable>
</template>npm install @askable-ui/svelte<script>
import { Askable, useAskable } from '@askable-ui/svelte';
const { promptContext } = useAskable();
export let kpi;
</script>
<Askable meta={{ metric: kpi.name, value: kpi.value }}>
<KPICard data={kpi} />
</Askable>npm install @askable-ui/coreimport { createAskableContext } from '@askable-ui/core';
const ctx = createAskableContext();
ctx.observe(document.body);
ctx.on('focus', () => {
console.log(ctx.toPromptContext());
// "User is focused on: — metric: revenue, value: $2.3M"
});- Docs: askable-ui.com/docs
- Analytics dashboard demo: askable-mu.vercel.app
| Guide | |
|---|---|
| Getting started | Install, observe, inject |
| Annotating elements | data-askable, nesting, priority |
| React · Vue · Svelte | Framework guides |
| CopilotKit integration | Context-in-input pattern |
| API reference | Full type docs |
AGENTS.md contains copy-pasteable instructions for Claude, Cursor, Codex, and similar tools. Drop it into your project root and your coding agent will know how to integrate askable-ui correctly — annotation patterns, passive vs explicit flows, sanitization, and common mistakes.
PRs welcome! See CONTRIBUTING.md for setup instructions.
MIT — see LICENSE