Fork me on GitHub

sharing here because there's loads of goodies shared by all sorts of folks in the replies:

Nom Nom Mousse09:01:42

I'm writing an app that is using a lot of d3 and libraries that add functionality to d3. I'd like to write the code that creates graphs in plain javascript to more easily get help with API usage and so on from the developers of said d3 libraries. Are there any great gotchas I should know about when using plain js in a ClojureScript project? I'm using shadow-cljs so I'll probably do it by using


one possible problem is externs. for CLJS we have externs inference which addresses this for CLJS sources. this is not done for JS sources so that may create issues. easy to fix but still something that might happen

Nom Nom Mousse09:01:32

As long as there are no non-fixable problems I'm happy 😄 I sure appreciate the stability of cljs when I see the churn in js-land. But for functions that use (quickly changing) javascript libraries and do not interact with anything else, I think it might be just as well to write those functions in js. I'm happy I have the opportunity to do so 🙂

Nom Nom Mousse09:01:21

Does anyone feel typescript is a win compared to js? Is it just as easy to use with clojurescript?


After having to work with TS for some time, I'm definitely hesitant to consider it for anything. And if you want to use TS to tie together JS and CLJS, then you will create a whole lot more problems for yourself.

🙏 1
👍 1

Even the lead maintainer of Cljs prefers to use JS to avoid the mess


I had to maintain and evolve a TS codebase for the past quarter. It is my first time with TS. In retrospect my general feeling is: not helpfull. I quickstarted a greenfield project this week, in cljs with the editor connected to the REPL and so on. I feel I just came back to life!

👍 1

Well I meant that not even the CLJS developer uses CLJS and prefers JS for real world projects :rolling_on_the_floor_laughing:

Nom Nom Mousse18:01:10

> not even the CLJS developer uses CLJS and prefers JS for real world projects :rolling_on_the_floor_laughing: @U01KZDMJ411 Do you have a cite for that? :D


Probably this: I personally disagree with some of the aspects of that approach, but judging by a few videos with David we write drastically different software in drastically different environments.

🙏 1
Nom Nom Mousse18:01:25

But he still uses Cljs? 😅 In my case, I really think using JS for graph components will be simpler (if JS and JS-libs were as backward-compatible as Cljs, I wouldn't bother.)


If by "graph components" you mean D3 and similar then yes, of course - because they were intended to be used with JS. :D If you use a CLJS library, it will be easier to use with CLJS. In general, if you compare two declarative libraries, the CLJS one should be more clean and clear in its usage.


FWIW, I think 1500 LoC is very small and overkill for Cljs


If you mean that David said that the overall project had just 1500 loc, then that project also had a bunch of CLJS dependencies.


Ok, must have missed that part


People went from Java -> Clojure. ClojureScript -> TypeScript seems very backwards 🙂

💯 2

And TS is, after all these years, still very much in flux. "How do I do X? Oh, it's impossible, but there's an issue for that..." is a very common thing in my experience.

Cora (she/her)16:01:35

I use typescript all day and I rarely if ever run into that

Cora (she/her)16:01:49

and I rather like TS

Cora (she/her)16:01:45

most of my complaints about the ecosystem are true of just about anything that uses js: build systems are wild and dependencies are out of control


I am making progress. I suspect I am not getting the node_modules included. I did try setting :npm-deps and can see it being passed to the closurescript compiler. I can see in the generated cljs_deps.js the generated goog.AddDependency which declares a dep from my .js to grpc.web.(things). Alas, in the console after loading, goog.require complains that grpc.web.MethodDescriptor is a bad path or symbol, as if it never found module grpc-web


Hi guys, I’m in a bit of a predicament here: so I’m currently maintaining two separate web apps/sites. One is written in Next JS and the reason for that is for maintaining a landing page and a developer blog currently deployed on Vercel. The second app is a proper re-frame clojurescript app that uses web3 that is deployed on DigitalOcean using its App Platform. My question is, I want to bring both of these applications under the same domain, but I’m not sure exactly what path to pick: do i merge the Next JS code into my re-frame app? Do i port the CLJS app to the Next JS app? Or do i do something like an nginx reverse proxy configuration so that both apps are served under the same domain?


unless there's some other reasons to rewrite either one of them, i would do the reverse proxy or some domain shenanigans


Do they need to be on the same subdomain? The easiest option is to put each under a different subdomain (eg. and


They don’t necessarily need to be


Alright I’ll try and get the nginx proxy working then


the subdomain approach doesn't require a reverse proxy. You can just create a DNS entry for each.

☝️ 1

hello i saw that top level await are possible now in latest nodejs, this means that clojurescript core.async, maybe sometime support <!! like clojure does? (i mean wait for async call in top level, outside of a go block)


no, that is still not blocking. its just integrated into the loading process, which is already async (its just internal to the engine)


is it something that it can change? so we can use sometime <!! in clojurescript also? (i hope question makes sense)


no, not anytime soon


ok thank you : )

Drew Verlee22:01:22

What tools allow for stepping through clojurescript similar to Edebug? Stepping, setting conditions, display expression results, etc... My go to forms of debugging currently are using vars and atoms to capture, and printing. Both are very useful, but sometimes you want to follow the data alone a path, and the standard step debugging can be very useful for this. It doesn't seem like editors support cljs debuggers: • Vscode + Calva: The debugger currently does not support ClojureScript • Intelliji + cursive: nope • Emacs + cider : cider doesn't support cljs debugging • vim: ??? Maybe there is a simpler way to just do a ray trace of sorts through the code?


I think cljs-devtools allows this in a browser environment, if you use a fork of the Chrome devtools. I believe it’s Chrome (and therefore browser) only though.


I don’t know what the state of that is, though.

👍 1

Yep, regular breakpoints work in Chrome just fine. Well, almost - some expressions turn into multiple JS statements so e.g. "step over" might seem to be stuck on the same Clojure expressions for multiple rounds.

👍 1
Lukas Domagala22:01:53

None of the IDE tools can do it for cljs, mostly because their debugger is based on nrepl and it can't do it. There's been some effort into it, but it's a hard problem and the debugger isn't used very much apparently. You can do a deep trace of cljs with postmortem or omni-trace

👍 1
Drew Verlee00:01:59

Thanks for the replies.


Step debugging work in all browsers since we generate source maps


cljs-devtools mostly enhances the experience


In other contexts (native) REPL is a life saver for sure

Drew Verlee04:01:41

Thanks. Yea, it seems i should get in a habit of just setting break points in the browser. I'll see how that goes from time to time 🙂


I’ve had it almost working in VS Code’s debugger with Calva. But it was too unreliable to be worth it.

👍 1
Richard Bowen22:01:33

Hey, can you assist me with translating this to ClojureScript (see first reply):

Richard Bowen22:01:03

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
      hidden={value !== index}
      {value === index && (
        <Box p={3}>

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.any.isRequired,
  value: PropTypes.any.isRequired,

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
    backgroundColor: theme.palette.background.paper,

export default function SimpleTabs() {
  const classes = useStyles();
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Tabs value={value} onChange={handleChange} aria-label="simple tabs example">
          <Tab label="Item One" {...a11yProps(0)} />
          <Tab label="Item Two" {...a11yProps(1)} />
          <Tab label="Item Three" {...a11yProps(2)} />
      <TabPanel value={value} index={0}>
        Item One
      <TabPanel value={value} index={1}>
        Item Two
      <TabPanel value={value} index={2}>
        Item Three

Richard Bowen22:01:48

For tab-panel, this is what I have:

(defn tab-panel [& args]
  (let [{:keys [children props]} (util/props args)
        {:keys [index value & other]} props]
    [:div (merge {:role "tabpanel"
                  :hidden (not= index value)
                  :id (str "tabpanel-" index)} other)
     (if (= index value)
       (into [box {:mt 3}] children))]))


You seem to be doing fine so far, although there's probably no need to call util/props above - you can just pass the relevant values directly to tab-panel and use a proper function signature instead of opaque & args. After all, you're the one using creating Hiccup vectors with tab-panel - meaning, you define how it's used. Are there things you're having troubles with? If it's React hooks than Reagent has a section on them in its React Features document in its repo.

Richard Bowen00:01:35

The issue I'm having is setting the tab value to the index of the tab that is clicked, thus changing the tab. I've tried :on-change #(reset! tab-value (-> % .-target .-value)) but upon inspection of % there's no value key.

Richard Bowen21:01:26

@U2FRKM4TW, okay, so pass index and value directly (defn tab-panel [index value]) correct? If so, how would I accommodate children, i.e. wrap child elements within the box component?


Children should be regular arguments as well: (defn tab-panel [index value & children] ...) Or: (defn tab-panel [{:keys [index value]} & children] ...) Or #2: (defn tab-panel [{;keys [index value children ]}] ...) As I said - it's a regular function. The more you can do without touching Reagent machinery, the better.

Richard Bowen05:01:14

@U2FRKM4TW, okay great, refactored. What about the issue with the :on-change event mentioned above, what other ways are there using reset! to update an atom tab-value ? I can't seem to find a key from % that has the tab-value :

[tabs {:value @tab-value
              :on-change #(js/console.log %)
              :variant "scrollable"
              :scroll-buttons (if mobile? "on" "off")
              :indicator-color "primary"}
        [tab {:label "1"}]
        [tab {:label "2"}]
        [tab {:label "3"}]
        [tab {:label "4"}]]


The onChange handler function takes two arguments in the original code. Yours takes only one.

Richard Bowen13:01:10

Thanks @U2FRKM4TW, fixed.

👍 1