Skip to content

Instantly share code, notes, and snippets.

<picture size-hint="100% 550px">
<source src="/images/hero~640x550@1x.jpg" width="640" height="550" resolution="1x"/>
<source src="/images/hero~640x550@2x.jpg" width="1280" height="1100" resolution="2x"/>
<source src="/images/hero~768x550@1x.jpg" width="768" height="550" resolution="1x"/>
<source src="/images/hero~768x550@2x.jpg" width="1536" height="1100" resolution="2x"/>
<source src="/images/hero~1024x550@1x.jpg" width="1024" height="550" resolution="1x"/>
<source src="/images/hero~1024x550@2x.jpg" width="2048" height="1100" resolution="2x"/>
<source src="/images/hero~1280x550@1x.jpg" width="1280" height="550" resolution="1x"/>
<source src="/images/hero~1280x550@2x.jpg" width="2560" height="1100" resolution="2x"/>
<source src="/images/hero~1536x550@1x.jpg" width="1536" height="550" resolution="1x"/>

Remix Conventions Proposals

I'm splitting these off into separate changes that could be made to Remix that I think would make it more flexible and provide more value.

.route. naming

Remix (any many other tools right now) contains what I'll call "magic exports":

Idea: Flat file system for file-based routing

Personally I've never liked how tools like Remix or NextJS have mapped a nested file system to routes. Simple things like "I want to put this component in its own file" become annoying tasks.

I've always been a fan of "flatter" file systems, my files often look like this:

/App/
function t(){let t=new Set;return{listen:e=>(t.add(e),()=>{t.delete(e)}),emit(){t.forEach((t=>t()))}}}function e(){return Math.random().toString(36).substring(2,8)}function n(t,e,n){return Math.min(Math.max(t,e),n)}function r(t,e){return(t.startsWith(e)?"":e)+t}function a({pathname:t,search:e,hash:n}){return{pathname:r(t,"/"),search:r(e,"?"),hash:r(n,"#")}}function s({pathname:t,search:e,hash:n}){return t+("?"===e?"":e)+("#"===n?"":n)}function h(t){return s(a(t))}function u(t){let[e,n=""]=t.split("#"),[r,s=""]=e.split("?");return a({pathname:r,search:s,hash:n})}function i(t){return s(u(t.substring(1)))}function o(t){if("string"==typeof t)return t;{let{pathname:e="",search:n="",hash:r=""}=t;return h({pathname:e,search:n,hash:r})}}function l(t){return{location(){return e=t.path(),n=t.state(),r=t.key(),{...u(e),state:n,key:r};var e,n,r},push(e,n){let r=o(e);t.push(r,n)},replace(e,n){let r=o(e);t.push(r,n)},go(e){t.go(e)},back(){t.go(-1)},forward(){t.go(1)},listen:e=>t.listen(e)}}function p(n){let r=n.history,a=t
function capitalize(locale, string) {
let segmenter = new Intl.Segmenter(locale, { granularity: "grapheme" })
let result = ""
for (let item of segmenter.segment(string)) {
if (item.index === 0) {
result += item.segment.toLocaleUpperCase(locale)
} else {
result += item.segment
}

Hard to type symbols (on non-US keyboards)

I asked on twitter which symbols are hardest to type on non-US keyboards.

I didn't get a ton of responses, but I think it was clear which ones were most problematic.

char count visual notes
& 4 ####
^ 10 ########## Swedish: Dead Key, German: Typing vowels after becomes â
// Destructuring works on arrays, sets, object, but not maps.
//
// Today [array, destructuring] works on sets because it
// uses [Symbol.iterator]
//
// But {object, destructuring} *can't* call and iterator
// because it doesn't have an order to work with.
//
// But this difference can lead to confusion for beginners,
// and makes maps less usable for everyone.
// Synchronously check if a promise has already been fulfilled
Promise.isFulfilled(promise)
// >> true | false
// This allow you to make decisions synchronously
if (Promise.isFulfilled(promise)) {
// We know we're not taking a "risk" awaiting this promise
await promise
} else {
// i.e. Avoid "flash of loading spinner"
// Syntax proposal:
spawn {
await somethingAsync()
}
// `spawn` would be available wherever `await` is available
async function fn() {
await somethingAsync()
spawn { /* ... */ }
}