Should I Use ~/ Instead of @/ for React Import?

When setting up a React project, one of the crucial decisions developers face is choosing between ~/ and @/ for import aliases. This choice might seem trivial at first, but it can significantly impact your codebase's clarity and maintainability. Recent discussions in the React community have highlighted growing concerns about import alias conventions, particularly regarding the ambiguity of using @/ when some node modules naturally begin with the @ symbol.

The Import Alias Dilemma

Picture this: You're working on a React project, and you need to import a component from a deeply nested directory. You're faced with two common approaches:

// Using @/
import { MyComponent } from '@/components/shared/MyComponent';

// Using ~/
import { MyComponent } from '~/components/shared/MyComponent';

Both methods aim to solve the same problem - avoiding the dreaded relative import hell that looks like this:

import { MyComponent } from '../../../../../components/shared/MyComponent';

Why Import Aliases Matter

The way you structure your imports isn't just about aesthetics; it directly affects:

  1. Code Readability: Clear import paths make it easier for developers to understand where components and utilities come from

  2. Maintainability: Well-structured imports make refactoring and code organization more manageable

  3. Developer Experience: Proper import conventions reduce cognitive load when navigating the codebase

The Case Against @/

The @/ prefix has been a popular choice in many React projects, but it comes with some notable drawbacks:

  1. Package Name Conflicts: As highlighted in recent developer discussions, many node modules naturally start with @ in their names. This creates potential ambiguity and confusion when distinguishing between local source files and external packages.

  2. Namespace Pollution: The @ symbol is commonly used for organization-scoped packages in npm. Using it for local imports can blur the line between internal and external dependencies.

One developer aptly noted: "Some node modules natively start with @ in their name. So this prefix isn't uniquely reserved for local source files."

The Rise of ~/

The tilde prefix (~/) has gained popularity as an alternative to @/ for several compelling reasons:

  1. Familiar Mental Model: Many developers find ~/ more intuitive because it mirrors the Unix/Linux convention where ~ represents the home directory. As one developer mentioned, "I found it is more like easier for me remembering it is like putting '~' (HOME) in linux console."

  2. Clear Distinction: Unlike @/, the ~/ prefix is rarely used in node module names, making it easier to distinguish between project imports and external packages.

  3. Visual Clarity: The tilde symbol stands out visually in code, making imports easy to spot and understand at a glance.

Setting Up Import Aliases

Whether you choose ~/ or @/, here's how to configure import aliases in your React project:

For Vite Projects

  1. Update your vite.config.js:

import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig({
  resolve: {
    alias: {
      '~': path.resolve(__dirname, 'src'), // or '@' if you prefer
    },
  },
});

For Create React App

  1. Add a jsconfig.json (or tsconfig.json for TypeScript) in your project root:

{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "~/*": ["./*"]  // or "@/*" if you prefer
    }
  }
}

Best Practices for Import Structure

Regardless of which prefix you choose, following these best practices will help maintain a clean and manageable codebase:

  1. Be Consistent: Choose one approach and stick to it throughout your project. Mixing different import styles can lead to confusion and maintenance headaches.

  2. Use Descriptive Paths: Structure your imports to reflect your application's architecture:

// Good
import { UserProfile } from '~/features/user/components/UserProfile';
import { authUtils } from '~/core/auth/utils';

// Avoid
import { UserProfile } from '~/components/UserProfile';
import { authUtils } from '~/utils';
  1. Group Related Imports: Keep imports organized by type and origin:

// External dependencies
import React from 'react';
import { useQuery } from 'react-query';

// Internal modules
import { UserProfile } from '~/features/user/components/UserProfile';
import { useAuth } from '~/core/auth/hooks';

// Styles
import styles from './styles.module.css';

Alternative Approaches

While ~/ and @/ are popular choices, some developers advocate for different approaches:

Self-Import Pattern

As suggested in recent community discussions, you can use your package name as the import prefix:

// package.json
{
  "name": "my-awesome-app"
}

// In your code
import { MyComponent } from 'my-awesome-app/components/MyComponent';

This approach:

  • Avoids conflicts with node modules

  • Provides clear ownership of the code

  • Makes it obvious which imports are internal vs external

Barrel Files

Another strategy is using barrel files (index.js) to simplify imports:

// /components/index.js
export { default as Button } from './Button';
export { default as Input } from './Input';

// In your code
import { Button, Input } from '~/components';

Making the Right Choice

When deciding between ~/ and @/, consider these factors:

  1. Team Preference: What's familiar to your team? As one developer noted, "Not sure if I'm in the minority but since editors auto import everything automatically nowadays, I've stopped caring about these sorts of pseudo-'absolute' imports."

  2. Project Context:

    • For smaller projects, either approach works well

    • For larger projects, consider the self-import pattern for better scalability

    • For monorepos, consistent conventions become even more critical

  3. Existing Codebase: If you're working on an established project, stick to its existing conventions unless there's a compelling reason to change.

Conclusion

While both ~/ and @/ serve the same purpose, the growing preference for ~/ in the React community stems from its clearer distinction from node module imports and its familiar Unix-like semantics. However, the most important factor is consistency within your project.

Remember these key takeaways:

  • Choose a convention that works for your team and stick to it

  • Consider the self-import pattern for larger projects

  • Focus on maintaining clear and consistent import structures

  • Document your chosen approach in your project's README

The best import alias is the one that makes your code more maintainable and your team more productive. Whether you choose ~/, @/, or another approach, the key is to be consistent and clear in your implementation.

Additional Resources

Raymond Yeh

Raymond Yeh

Published on 13 March 2025

Get engineers' time back from marketing!

Don't let managing a blog on your site get in the way of your core product.

Wisp empowers your marketing team to create and manage content on your website without consuming more engineering hours.

Get started in few lines of codes.

Choosing a CMS
Related Posts
How Should I Organize My Types as a React Developer?

How Should I Organize My Types as a React Developer?

Struggling with messy API responses and scattered type definitions? Learn the battle-tested approach to organizing your React types, from co-location principles to handling inconsistent APIs.

Read Full Story
npm vs pnpm: Which Package Manager Should You Choose?

npm vs pnpm: Which Package Manager Should You Choose?

Compare npm vs pnpm performance, storage efficiency, and compatibility. Make an informed choice for your JavaScript projects with real developer insights.

Read Full Story
Starting a New Next.js 14 Project: Should You Use App Router or Page Router?

Starting a New Next.js 14 Project: Should You Use App Router or Page Router?

Next.js 14: App Router vs Page Router? Discover the key differences and find out which routing solution best suits your dynamic web project.

Read Full Story
Loading...