Siema: the author calls carousels an anti-pattern, then kept committing for six years
GitHub describes it as Siema - Lightweight and simple carousel in pure JavaScript. The repository metadata lists JavaScript as its primary language. The metadata lists the NOASSERTION license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- pawelgrzybek/siema is a 3kb dependency-free JavaScript carousel whose README opens by telling you not to use it and points questions at its closed issues, whose manifest has said version 1.5.1 since February 2018 across six years of later commits, and whose multipleDrag option defaults to true with no example.
- Who is it for?
- Read it if you are studying a small, dependency-free carousel or you already depend on it and need to understand what you inherited, but pick something else for new work, which is what the author asks. Two things to check before you rely on either.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Probably not. The repository last received commits 25 months ago, on September 23, 2024.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Star history
- pawelgrzybek/siema3,478 ★ · -1
| Repository | 2026-08-13 | 2026-10-22 |
|---|---|---|
| pawelgrzybek/siema | 3.5k | 3.5k |
-1 Stars in the last 70 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The author asks you not to use it, and points questions at the closed issues
The first paragraph of this README is not an introduction. It is the author telling readers he is stopping work on the library, and it does that before the title appears.
He says he built it for himself, for one of his own projects, and that two years later he considers carousels an anti-pattern. He suggests finding a better interface pattern for whatever you were going to use it for. Then he says that if you really want it, feel free, and that if you have questions the answer is in the closed issues section. Finally he asks whether anyone wants to contribute or continue maintenance, and answers his own question with the word Fantastic.
That is an unusually honest opening, and the specific instruction about support is the part worth remembering: the designated place to get an answer is a set of issues that are already closed.
The invitation to take over contains a typo. It asks whether you would like to contribute or coutinue maintenance, with the last letter group transposed. It is in the single sentence that exists to recruit a successor.
The timing is the other half of this. The notice describes the project as two years old, which places it around 2018, and the three releases on record were all published in February of that year. The last commit to the default branch is dated 2024-09-23. So the repository received roughly six and a half years of commits after its author publicly asked someone else to maintain it.
Nobody did, and the project was not archived. What that means for you is that the notice at the top is a request, not a closure.
Version 1.5.1 has been the current version since February 2018
Three releases exist. One point four fourteen, one point five, and one point five one. They were published on the 7th, 12th and 14th of February 2018, three tags inside eight days.
That was the last release. The manifest still declares version 1.5.1, which means the version has not moved in all the time since.
So there are two things a user can obtain and they are not the same thing. The package on the registry is 1.5.1, built in February 2018. A clone of the default branch is also version 1.5.1, built from code that has been committed to as late as September 2024.
Nothing in either artifact records the difference. There is no build number, no commit hash baked into the distributed file, no nightly channel and no canary. A reader who installs the package and a reader who clones and builds see the same version string and have no way to tell which they are looking at.
That matters more than it sounds for a library whose selling point is that it is three kilobytes gzipped with no dependencies. The size claim and the option list are both tied to a specific 2018 build, and neither has been re-measured since.
For a project being taken over, the version number is the first thing that needs a decision: keep 1.5.1 and confuse people, or cut a real release and make the six years of commits reachable.
multipleDrag defaults to true and is the only option with no example
Twelve options, and the README gives all twelve defaults in one block:
new Siema({
selector: '.siema',
duration: 200,
easing: 'ease-out',
perPage: 1,
startIndex: 0,
draggable: true,
multipleDrag: true,
threshold: 20,
loop: false,
rtl: false,
onInit: () => {},
onChange: () => {},
});Eleven of the twelve have a worked example linked from their description. The twelfth does not.
That twelfth option is multipleDrag, described as allowing dragging to move multiple slides, and it is the only entry in the options list without an example link. It also defaults to true.
Read alongside its neighbours, that default is the aggressive one. Duration is 200 milliseconds, per page is one, and the drag threshold is 20 pixels. So out of the box a short flick can move more than one slide, and the documentation does not say how many slides a flick of a given length advances, nor when the multiple behaviour engages rather than the single one.
A carousel that overshoots on a flick is the kind of defect that gets reported as your bug rather than the library's, so the absence of an example here costs more than it would for a more self-evident option.
One more option is under-specified in the same way, though it does have a link. perPage accepts a number or an object, described as being for complex responsive layouts. The shape of that object is never written down, so the most capable option in the list is the one you would most want a table for.
There is no method that re-reads the DOM
The API is nine methods and one property. Previous and next, each taking how many slides and an optional callback. Go to a particular index. Remove the item at an index. Insert an item at an index. Prepend. Append. Destroy, with an optional flag to restore the original markup. And a property that reports the current index.
Six of the nine take an optional callback, which is a consistent and pleasant convention. Insert and remove take an index rather than a reference, so the caller has to track positions as the list changes.
The shape of the whole API is what is worth noticing. Every method is imperative: it does something to the carousel's internal list. None of them reads the container. There is no update, no refresh, no resync, no setter for the slide list.
So if you add an element to the container yourself, or a framework re-renders it, or a single-page route swaps the contents, the carousel has no way to learn. The documented path is to call insert or append, which means the container and the carousel's state can drift apart, and the only method that addresses the DOM directly is destroy with the restore flag set.
That flag is worth understanding too. Destroy's first argument defaults to false, so the default call removes every event listener and leaves your slides in whatever state the carousel left them in. Restoring the initial markup is something you have to ask for.
Two lockfiles, a version 7 loader paired with version 6 of Babel
The build toolchain is entirely 2017 and 2018 vintage, and one entry in it does not line up.
The manifest declares four Babel-related packages plus a loader. Three of the Babel packages are at major version 6: the umbrella package, the core package, and an ES2015 preset, all on the 6.x line. The loader is at major version 7.
The version 7 loader is the one that expects version 7 of Babel. So the manifest pairs a loader from the newer major line with the older packages it is meant to drive. That is the kind of mismatch that either works because the older core happens to expose what the loader needs, or fails at build time, and either way it is not a combination a maintainer picking this up would design on purpose.
Two lockfiles sit at the top level, one for each of the two package managers the README and the scripts both reference. The install instructions tell you to use yarn, the dev script uses no package manager, and both lockfiles are committed. Two lockfiles cannot both be authoritative, and the published package contains neither, because the files list in the manifest ships only the built directory. So the ambiguity only ever affects contributors, which is exactly who will hit it first.
The linter is on a legacy configuration file and a version 4 release, and the bundler is on version 3. Both of those majors predate the flat configuration formats the same tools now default to.
The published entry point is a pre-built bundle with no module build
The manifest's main field points at the minified file inside the built directory, and the files list ships only that directory. So a consumer installing the package receives one bundled JavaScript file and nothing else.
There is no module field. There is no separate ES module entry. The library's own installation instructions say to use it with your favourite module bundler, and then show importing it by name, which resolves to the pre-bundled artifact rather than to source.
That has a cost worth naming. A consumer's bundler receives a file that another bundler already processed, which means the consumer cannot tree-shake it, gets whatever transpilation the library's own build chose, and has no ability to swap in their own build configuration. The library's headline property, that it has no dependencies and is three kilobytes gzipped, is true, and it is achieved by shipping the output of a specific build rather than the code.
The manual route has the same shape with less ceremony:
<script src="siema.min.js"></script>
<script>
new Siema();
</script>There is no version in that path, no integrity attribute and no CDN URL, so the manual route is the one with the least tooling and the least pinning. A script tag whose source is a relative filename resolves against whatever path your page happens to be served from, so getting this working from a file on disk and from a deployed site are two different problems.
The build script does one more thing worth knowing: it runs the bundler and then copies the resulting file into the documentation directory. So the docs site serves the same artifact you would install, which is a good property, and also means a docs rebuild is the only thing that ever refreshes it.
A library with nine methods and no test script
The manifest has two scripts. One builds, and the other runs the bundler in watch mode. There is no test script, no test directory in the repository listing, and no test framework in the development dependencies.
So a library with twelve options, nine methods and a documented browser support matrix has no automated tests. Nothing in the repository asserts that the default option values still produce the default behaviour, or that destroy restores markup, or that the currentSlide index stays correct after a remove and an insert.
That matters more here than it would for a library with a narrow surface, because the API lets callers mutate the slide list at arbitrary positions. Remove at an index, then insert at an index, and the correct index for the next operation is the sort of thing a test suite exists to pin down, and there is nothing pinning it down.
The browser support list compounds it. Seven entries, all of them minimum versions: one ten, twelve, sixteen, fifteen, five point one, four point zero, six point zero. Every one of those numbers is from 2012 or 2013. That is a coherent floor for a library transpiled to ES2015 and it is honest about being a floor rather than a tested set, but nothing in the README says what happens on a current browser, and no test ever ran on one.
The one piece of testing infrastructure the repository does have is external. The credits thank a browser testing service for free access, which is how a 2018 single-author project checked seven browser versions.
Editorial conclusion
Read it if you are studying a small, dependency-free carousel or you already depend on it and need to understand what you inherited, but pick something else for new work, which is what the author asks. Two things to check before you rely on either. The published package and a build from the default branch are both version 1.5.1 and there is nothing distinguishing them, so a clone is not the same code as an install. And the API is entirely imperative with no method that re-reads the container, so anything that mutates the markup behind the carousel's back will drift. The stated support floor is Internet Explorer 10, and there is no test suite to tell you what actually works.
Frequently asked questions
How do I install Siema?
Three routes. Markup: a div with the class siema containing one child element per slide. A module bundler: yarn add siema, then import Siema from siema and call new Siema. Or manual injection: a script tag pointing at siema.min.js and a call to new Siema.
How large is the Siema carousel?
The README describes it as a lightweight carousel plugin, only 3kb gzipped, with no dependencies and no styling. The size figure comes from the minified bundle the manifest points at, and it has not been restated since the 2018 releases.
What options does Siema accept?
Twelve: selector, duration, easing, perPage, startIndex, draggable, multipleDrag, threshold, loop, rtl, onInit and onChange, with all twelve defaults shown in a single block. Eleven have a worked example linked; multipleDrag does not, and it defaults to true.
Which methods does the Siema API expose?
Nine methods plus a property: prev, next, goTo, remove, insert, prepend, append and destroy, each taking an optional callback, and currentSlide for the index. destroy takes a restoreMarkup argument that defaults to false. There is no method that re-reads the container.
Is Siema still being worked on?
The README's opening paragraph says the author is discontinuing active maintenance, considers carousels an anti-pattern and suggests a different interface pattern, while inviting contributions or a new maintainer. It also says to look for answers in the closed issues. The last commit is dated 2024-09-23 and the newest release is v1.5.1 from 2018-02-14.
Which browsers does Siema support?
IE10, Chrome 12, Firefox 16, Opera 15, Safari 5.1, Android Browser 4.0 and iOS Safari 6.0. Those are stated as minimum versions, so the floor is a 2012 to 2013 browser baseline rather than a tested set.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pawelgrzybek-siema)Star history chart for your README
[](https://hysenlabs.com/projects/pawelgrzybek-siema)