npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lincd

v1.0.3

Published

LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)

Readme

LINCD.js

Linked INteropeable Code & Data

All the tools you need to build Linked Data applications with React

LINCD.js is a modern TypeScript library for building React applications powered by RDF and SHACL Shapes. Create clean, type-safe code that queries, creates, updates, and deletes linked data through object-oriented APIs, with automatic data loading and reactive components.

New to Linked Data? It is a W3C standard based on RDF used to build interconnected knowledge graphs. It is also known as Structured Data which can help search engines to present rich snippets in search results.

LINCD.js is compatible with the RDFJS data model

See also

LINCD offers:

Why?

LINCD is built from the ground up with a modern tech stack and integrates the latest developments. This allows us to offer advanced features that go beyond other existing RDF JavaScript libraries. Whilst also offering solutions for some major challenges in the world of Linked Data:

Making Linked Data app development easy

Graph databases are being adopted everywhere. Tons of Open Linked Data is being published. Tools have matured, but the learning curve for developers is still steep.

LINCD significantly reduces the amount of learning required and makes it much easier to work with Linked Data.

Reusable code for Linked Data

There are tons of ontologies (reusable linked data structures) available. But without a good searchable registry, it's hard to find the right one. And starting to use a specific ontology can be time-consuming still. Reusable UI components built specifically for Linked Data are virtually non-existent.

LINCD.org offers an open registry of quality ontologies and UI components built for those ontologies. Each of these ontology and components can be imported and used with just a few lines of code. This library makes it easy to develop and share such ontologies and components in the registry.

Solving data validation & more

Another hurdle in the adoption of Linked Data has been the openness of the RDF model. With a lack of proper data restriction and data validation tools, maintaining a clean dataset in real life applications has been a challenge. W3C has since published SHACL, which is an excellent standard to tackle this. However, the tools to work with SHACL have been minimal.

LINCD goes full in on SHACL and places SHACL's data "Shapes" right at the center of development. This creates much simpler and cleaner code, frees developers up from thinking about which classes & properties to use and allows us to offer advanced features like automatic data loading and data validation that to our knowledge no other Linked Data library or framework offers.

Installation

npm + node.js

npm install lincd
import { Shape, linkedComponent } from 'lincd';

See this tutorial on how to build linked data applications with LINCD.js

Schema-Parameterized Query DSL

LINCD Queries is a TypeScript-embedded declarative query language that compiles its query AST to backends like SPARQL. You define your own SHACL shape schemas, making the DSL schema-parameterized and domain-agnostic — you apply the generic DSL to your specific domain.

The query language provides a type-safe, object-oriented interface that works with any domain through custom Shape classes:

// Basic property selection
let names = await Person.select(p => p.name);

// Filtering with where clauses
let filtered = await Person.select().where(p => p.name.equals('Semmy'));

// Nested property access
let friendsNames = await Person.select(p => p.friends.name);

// Complex filtering with AND/OR
let friends = await Person.select(p => 
  p.friends.where(f => 
    f.name.equals('Moa').and(f.hobby.equals('Jogging'))
  )
);

// Aggregation
let friendCounts = await Person.select(p => p.friends.size());

// Sub-queries
let detailedFriends = await Person.select(p => 
  p.friends.select(f => ({ name: f.name, hobby: f.hobby }))
);

The query DSL automatically converts to backend query languages (like SPARQL) and executes against your configured storage layer.

Shapes

LINCD introduces Shape classes that generate SHACL Shapes.

These classes enable automatic data validation and abstract away RDF implementation details.

Consider this example shape:

Definition

@linkedShape
export class Person extends Shape {
 /**
  * indicates that instances of this shape need to have this rdf.type
  */
  static targetClass: NamedNode = foaf.Person;

 /**
  * instances of this person shape are REQUIRED to have exactly one foaf.name property
  */
  @literalProperty({
    path: foaf.name,
    required: true,
    maxCount: 1,
  })
  get name() {
    return this.getValue(foaf.name);
  }
  set name(val: string) {
    this.overwrite(foaf.name, new Literal(val));
  }

  /**
   * values of the property foaf.knows needs to be valid instances of this Person shape as well
   */
  @objectProperty({
    path: foaf.knows,
    shape: Person,
  })
  get knows(): ShapeSet<Person> {
    return Person.getSetOf(this.getAll(foaf.knows));
  }
}

Usage

Now when we use the shape above, we can simply use plain and simple object-oriented code. The code below creates RDF triples in the graph, but it does not need to know about any specific RDF properties or classes to use.

Instead, it simply uses the accessors from the Shape.

Furthermore, the shape class allows us to validate our graph. This happens automatically when visualizing specific shapes from the graph with Linked Components

let person = new Person();
person.name = "Rene";

let person2 = new Person();
person2.name = "Jenny";

person.knows.add(person2);

//both persons are valid instances of Person
console.log(Person.validate(person)); //true
console.log(Person.validate(person2));//true

Automatic Data Loading

LINCD provides a flexible storage layer that can connect to multiple backend types. Components automatically load the data they need based on their queries — you just specify what data to load, and LINCD handles fetching it from your configured storage.

Storage Configuration

Storage is configured via storage-config.ts or storage-config.js:

// storage-config.js
import { FusekiStore } from 'lincd-fuseki/shapes/FusekiStore';
import { LinkedStorage } from 'lincd/utils/LinkedStorage';

let quadStore = new FusekiStore('cn-core', process.env.FUSEKI_BASE_URL);
LinkedStorage.setDefaultStore(quadStore);
LinkedStorage.init();

Storage Backend Options

LINCD supports multiple storage backends:

  • Node.js Backend → Enterprise Graph Databases: Connect to Virtuoso, GraphDB, Jena/Fuseki via LINCD server packages:

    • lincd-fuseki - Jena Fuseki connector
    • lincd-server - Node.js server with file storage
    • Other connectors as available
  • In-Memory Database: Node.js in-memory graph database for development and testing

  • Remote SPARQL Endpoint: Connect frontend directly to SPARQL endpoints (e.g., Fluree with advanced access rights)

  • Browser Storage: Store graph in browser localStorage for client-side applications

How Automatic Loading Works

When you use a Linked Component with a query, LINCD:

  1. Analyzes the component's query to determine what data is needed
  2. Checks if the data is already cached
  3. Automatically fetches missing data from your configured storage backend
  4. Converts the data into props for your component
  5. Caches results using a shape-aware query cache

The subject of the query depends on the of prop you pass to the component. For example, <PersonCard of={{id: me.id}} /> will automatically load data for the node with that URI.

React Linked Components

React Linked Components are UI components tied to LINCD queries. They automatically load data from your configured storage, convert query results to props, and handle smart caching.

Currently LINCD exclusively supports React components, though other libraries may be added in the future.

Creating a Linked Component

import { linkedComponent } from 'lincd';
import { Person } from './shapes/Person';

const PersonCard = linkedComponent(
  Person.query(p => ({ 
    name: p.name, 
    friends: p.friends.name,
    isLoading
  })),
  ({ name, friends }) => {
    return (
      <div>
        <h1>{name}</h1>
        <ul>
          {friends?.map(friend => (
            <li key={friend.id}>{friend.name}</li>
          ))}
        </ul>
      </div>
    );
  }
);

Using a Linked Component

// Automatically loads data for the specified node
<PersonCard of={{id: 'http://example.com/#me'}} />

// Or with a Shape instance
let person = new Person('http://example.com/#me');
<PersonCard of={person} />

How It Works

  1. Automatic Data Loading: When the component mounts, LINCD analyzes the query and automatically fetches the required data from your storage backend.

  2. Data Conversion: Query results are automatically converted to plain JavaScript objects and passed as linkedData props.

  3. Smart Caching: LINCD uses a shape-aware query cache. Components sharing the same shape data will share cached results.

  4. Subject from Props: The of prop determines which node to load data for. You can pass:

    • A QResult object: {id: 'http://example.com/#me'}
    • A Shape instance: new Person('http://example.com/#me')
    • A Node: NamedNode.getOrCreate('http://example.com/#me')

Reactive Queries

LINCD provides a reactive data layer at the query level — similar to what Svelte, Solid, or Apollo call "reactivity," but operating at the query level rather than component state level.

How Reactive Queries Work

Reactive Queries automatically update all components that depend on the same shape data whenever that data changes — powered by LINCD's shape-aware query cache.

When two components query the same shape data:

// Component 1
<PersonCard of={{id: me.id}} />

// Component 2 (elsewhere in your app)
<PersonProfile of={{id: me.id}} />

If one component updates the data (e.g., changes the person's name), the other component will automatically rerender with the updated data because they share the same cached query result.

This is made possible by LINCD's shape-aware query cache, which tracks which components depend on which shape properties and invalidates the appropriate caches when data changes.

Note: Reactive Queries implementation is coming soon. Once implemented, this will enable true reactive data synchronization across components.

Automatic Data Validation

Once you have linked your component to specific shapes (data structures), LINCD.js ensures that only those nodes in the graph that match with this shape will be allowed to be used with this component. Because of this, you can rest assured that all the data will be there and in the right format.

That is, PersonView will only render once LINCD has confirmed that the provided person instance is a valid instance of the Person shape. With the Person example above, this would mean it has exactly one name defined under person.name and person.knows returns a set of valid Person instances.

Validation happens automatically when:

  • Creating new Shape instances
  • Loading data from storage
  • Using Linked Components
  • Executing queries

Queries: Create, Update, Delete

LINCD provides object-oriented methods for creating, updating, and deleting RDF data:

Create

let newPerson = await Person.create({
  name: 'Alice',
  knows: [{id: existingPerson.id}]
});

Update

await Person.update({id: personId}, {
  name: 'Alice Updated',
  hobby: 'Reading'
});

Delete

await Person.delete({id: personId});
// or delete multiple
await Person.delete([{id: id1}, {id: id2}]);

All operations use your Shape classes and are validated against SHACL constraints.

A registry of reusable UI components, Shapes & ontologies

LINCD.org is a registry of components, shapes and ontologies built with LINCD.js. Each of these can be imported and used with a few lines. This is the best place to get started to build an application powered by linked data.

Documentation

See docs.lincd.org for the full documentation plus helpful tutorials for LINCD.js

Examples

See lincd.org/examples for a list of examples with source code on github.

Contributing

To make changes to lincd.js, clone this repository and install dependencies with npm install. Then run npm run dev to start the development process which watches for source file changes in src and automatically updates the individual transpiled files in the lib and the bundles in the dist folder.

Alternatively run npm run build to build the project just once.

We welcome pull requests.

License

MPL v2